diff --git a/contracts/core/Allo.sol b/contracts/core/Allo.sol index 7e69ffd59..749266922 100644 --- a/contracts/core/Allo.sol +++ b/contracts/core/Allo.sol @@ -46,7 +46,7 @@ contract Allo is IAllo, Native, Initializable, Ownable, AccessControlUpgradeable /// percentage is 1e17 (10%), then 100 DAI will be deducted from the 1000 DAI and the pool will be /// funded with 900 DAI. The fee is then sent to the treasury address. /// @dev How the percentage is represented in our contracts: 1e18 = 100%, 1e17 = 10%, 1e16 = 1%, 1e15 = 0.1% - uint256 private percentFee; + uint256 internal percentFee; /// @notice Fee Allo charges for all pools on creation /// @dev This is different from the 'percentFee' in that this is a flat fee and not a percentage. So if you want to create a pool @@ -55,28 +55,28 @@ contract Allo is IAllo, Native, Initializable, Ownable, AccessControlUpgradeable uint256 internal baseFee; /// @notice Incremental index to track the pools created - uint256 private _poolIndex; + uint256 internal _poolIndex; /// @notice Allo treasury - address payable private treasury; + address payable internal treasury; /// @notice Registry contract - IRegistry private registry; + IRegistry internal registry; /// @notice Maps the `_msgSender` to a `nonce` to prevent duplicates /// @dev '_msgSender' -> 'nonce' for cloning strategies - mapping(address => uint256) private _nonces; + mapping(address => uint256) internal _nonces; /// @notice Maps the pool ID to the pool details /// @dev 'Pool.id' -> 'Pool' - mapping(uint256 => Pool) private pools; + mapping(uint256 => Pool) internal pools; /// @custom:oz-upgrades-renamed-from cloneableStrategies - mapping(address => bool) private _unusedSlot; + mapping(address => bool) internal _unusedSlot; /// @notice The trusted forwarder contract address /// @dev Based on ERC2771ContextUpgradeable OZ contracts - address private _trustedForwarder; + address internal _trustedForwarder; // ==================================== // =========== Initializer ============= diff --git a/test/unit/core/AlloUnit.t.sol b/test/unit/core/AlloUnit.t.sol index 6bd8e21e8..0b0ce5328 100644 --- a/test/unit/core/AlloUnit.t.sol +++ b/test/unit/core/AlloUnit.t.sol @@ -2,117 +2,402 @@ pragma solidity ^0.8.19; import {Test} from "forge-std/Test.sol"; +import {MockMockAllo} from "test/smock/MockMockAllo.sol"; +import {Errors} from "contracts/core/libraries/Errors.sol"; +import {Metadata} from "contracts/core/libraries/Metadata.sol"; +import {IBaseStrategy} from "contracts/strategies/IBaseStrategy.sol"; +import {ClonesUpgradeable} from "openzeppelin-contracts-upgradeable/contracts/proxy/ClonesUpgradeable.sol"; +import {Ownable} from "solady/auth/Ownable.sol"; +import {LibString} from "solady/utils/LibString.sol"; contract Allo is Test { - function test_InitializeGivenUpgradeVersionIsCorrect() external { + using LibString for uint256; + + MockMockAllo allo; + + event PoolMetadataUpdated(uint256 indexed poolId, Metadata metadata); + + function setUp() public virtual { + allo = new MockMockAllo(); + } + + function test_InitializeGivenUpgradeVersionIsCorrect( + address _owner, + address _registry, + address payable _treasury, + uint256 _percentFee, + uint256 _baseFee, + address _trustedForwarder + ) external { + allo.mock_call__updateRegistry(_registry); + allo.mock_call__updateTreasury(_treasury); + allo.mock_call__updatePercentFee(_percentFee); + allo.mock_call__updateBaseFee(_baseFee); + allo.mock_call__updateTrustedForwarder(_trustedForwarder); + // it should call _initializeOwner + allo.expectCall__initializeOwner(_owner); + // it should call _updateRegistry + allo.expectCall__updateRegistry(_registry); + // it should call _updateTreasury + allo.expectCall__updateTreasury(_treasury); + // it should call _updatePercentFee + allo.expectCall__updatePercentFee(_percentFee); + // it should call _updateBaseFee + allo.expectCall__updateBaseFee(_baseFee); + // it should call _updateTrustedForwarder - vm.skip(true); + allo.expectCall__updateTrustedForwarder(_trustedForwarder); + + allo.initialize(_owner, _registry, _treasury, _percentFee, _baseFee, _trustedForwarder); } - function test_CreatePoolWithCustomStrategyRevertWhen_StrategyIsZeroAddress() external { + function test_CreatePoolWithCustomStrategyRevertWhen_StrategyIsZeroAddress( + bytes32 _profileId, + bytes memory _initStrategyData, + address _token, + uint256 _amount, + Metadata memory _metadata, + address[] memory _managers + ) external { // it should revert - vm.skip(true); - } + vm.expectRevert(Errors.ZERO_ADDRESS.selector); + + allo.createPoolWithCustomStrategy( + _profileId, address(0), _initStrategyData, _token, _amount, _metadata, _managers + ); + } + + function test_CreatePoolWithCustomStrategyWhenCallingWithProperParams( + address _creator, + uint256 _msgValue, + bytes32 _profileId, + IBaseStrategy _strategy, + bytes memory _initStrategyData, + address _token, + uint256 _amount, + Metadata memory _metadata, + address[] memory _managers, + uint256 _poolId + ) external { + vm.assume(address(_strategy) != address(0)); + vm.assume(_creator != address(0)); + allo.mock_call__createPool( + _creator, + _msgValue, + _profileId, + _strategy, + _initStrategyData, + _token, + _amount, + _metadata, + _managers, + _poolId + ); - function test_CreatePoolWithCustomStrategyWhenCallingWithProperParams() external { // it should call _createPool + allo.expectCall__createPool( + _creator, _msgValue, _profileId, _strategy, _initStrategyData, _token, _amount, _metadata, _managers + ); + + deal(_creator, _msgValue); + vm.prank(_creator); + uint256 poolId = allo.createPoolWithCustomStrategy{value: _msgValue}( + _profileId, address(_strategy), _initStrategyData, _token, _amount, _metadata, _managers + ); + // it should return poolId - vm.skip(true); + assertEq(poolId, _poolId); } - function test_CreatePoolRevertWhen_StrategyIsZeroAddress() external { + function test_CreatePoolRevertWhen_StrategyIsZeroAddress( + bytes32 _profileId, + bytes memory _initStrategyData, + address _token, + uint256 _amount, + Metadata memory _metadata, + address[] memory _managers + ) external { // it should revert - vm.skip(true); - } + vm.expectRevert(Errors.ZERO_ADDRESS.selector); + + allo.createPool(_profileId, address(0), _initStrategyData, _token, _amount, _metadata, _managers); + } + + function test_CreatePoolWhenCallingWithProperParams( + address _creator, + uint256 _msgValue, + bytes32 _profileId, + IBaseStrategy _strategy, + bytes memory _initStrategyData, + address _token, + uint256 _amount, + Metadata memory _metadata, + address[] memory _managers, + uint256 _poolId + ) external { + vm.assume(address(_strategy) != address(0)); + vm.assume(_creator != address(0)); + + address _expectedClonedStrategy = ClonesUpgradeable.predictDeterministicAddress( + address(_strategy), keccak256(abi.encodePacked(_creator, uint256(0))), address(allo) + ); + + allo.mock_call__createPool( + _creator, + _msgValue, + _profileId, + IBaseStrategy(_expectedClonedStrategy), + _initStrategyData, + _token, + _amount, + _metadata, + _managers, + _poolId + ); - function test_CreatePoolWhenCallingWithProperParams() external { // it should call _createPool + allo.expectCall__createPool( + _creator, + _msgValue, + _profileId, + IBaseStrategy(_expectedClonedStrategy), + _initStrategyData, + _token, + _amount, + _metadata, + _managers + ); + + deal(_creator, _msgValue); + vm.prank(_creator); + uint256 poolId = allo.createPool{value: _msgValue}( + _profileId, address(_strategy), _initStrategyData, _token, _amount, _metadata, _managers + ); + // it should return poolId - vm.skip(true); + assertEq(poolId, _poolId); } - function test_UpdatePoolMetadataGivenSenderIsManagerOfPool() external { + function test_UpdatePoolMetadataGivenSenderIsManagerOfPool(uint256 _poolId, Metadata memory _metadata) external { + allo.mock_call__checkOnlyPoolManager(_poolId, address(this)); + // it should call _checkOnlyPoolManager - // it should update metadata + allo.expectCall__checkOnlyPoolManager(_poolId, address(this)); + // it should emit event - vm.skip(true); + vm.expectEmit(); + emit PoolMetadataUpdated(_poolId, _metadata); + + allo.updatePoolMetadata(_poolId, _metadata); + + // it should update metadata + assertEq(allo.getPool(_poolId).metadata.pointer, _metadata.pointer); + assertEq(allo.getPool(_poolId).metadata.protocol, _metadata.protocol); } - function test_UpdateRegistryRevertWhen_SenderIsNotOwner() external { + function test_UpdateRegistryRevertWhen_SenderIsNotOwner(address _caller, address _registry) external { + vm.assume(_caller != address(0)); // owner is not set so is eq to 0 + // it should revert - vm.skip(true); + vm.expectRevert(Ownable.Unauthorized.selector); + + vm.prank(_caller); + allo.updateRegistry(_registry); } - function test_UpdateRegistryWhenSenderIsOwner() external { + function test_UpdateRegistryWhenSenderIsOwner(address _caller, address _registry) external { + vm.assume(_caller != address(0)); + + vm.prank(address(0)); + allo.transferOwnership(_caller); + + allo.mock_call__updateRegistry(_registry); + // it should call _updateRegistry - vm.skip(true); + allo.expectCall__updateRegistry(_registry); + + vm.prank(_caller); + allo.updateRegistry(_registry); } - function test_UpdateTreasuryRevertWhen_SenderIsNotOwner() external { + function test_UpdateTreasuryRevertWhen_SenderIsNotOwner(address _caller, address payable _treasury) external { + vm.assume(_caller != address(0)); // owner is not set so is eq to 0 + // it should revert - vm.skip(true); + vm.expectRevert(Ownable.Unauthorized.selector); + + vm.prank(_caller); + allo.updateTreasury(_treasury); } - function test_UpdateTreasuryWhenSenderIsOwner() external { + function test_UpdateTreasuryWhenSenderIsOwner(address _caller, address payable _treasury) external { + vm.assume(_caller != address(0)); + + vm.prank(address(0)); + allo.transferOwnership(_caller); + + allo.mock_call__updateTreasury(_treasury); + // it should call _updateTreasury - vm.skip(true); + allo.expectCall__updateTreasury(_treasury); + + vm.prank(_caller); + allo.updateTreasury(_treasury); } - function test_UpdatePercentFeeRevertWhen_SenderIsNotOwner() external { + function test_UpdatePercentFeeRevertWhen_SenderIsNotOwner(address _caller, uint256 _percentFee) external { + vm.assume(_caller != address(0)); // owner is not set so is eq to 0 + // it should revert - vm.skip(true); + vm.expectRevert(Ownable.Unauthorized.selector); + + vm.prank(_caller); + allo.updatePercentFee(_percentFee); } - function test_UpdatePercentFeeWhenSenderIsOwner() external { + function test_UpdatePercentFeeWhenSenderIsOwner(address _caller, uint256 _percentFee) external { + vm.assume(_caller != address(0)); + + vm.prank(address(0)); + allo.transferOwnership(_caller); + + allo.mock_call__updatePercentFee(_percentFee); + // it should call _updatePercentFee - vm.skip(true); + allo.expectCall__updatePercentFee(_percentFee); + + vm.prank(_caller); + allo.updatePercentFee(_percentFee); } - function test_UpdateBaseFeeRevertWhen_SenderIsNotOwner() external { + function test_UpdateBaseFeeRevertWhen_SenderIsNotOwner(address _caller, uint256 _baseFee) external { + vm.assume(_caller != address(0)); // owner is not set so is eq to 0 + // it should revert - vm.skip(true); + vm.expectRevert(Ownable.Unauthorized.selector); + + vm.prank(_caller); + allo.updateBaseFee(_baseFee); } - function test_UpdateBaseFeeWhenSenderIsOwner() external { + function test_UpdateBaseFeeWhenSenderIsOwner(address _caller, uint256 _baseFee) external { + vm.assume(_caller != address(0)); + + vm.prank(address(0)); + allo.transferOwnership(_caller); + + allo.mock_call__updateBaseFee(_baseFee); + // it should call _updateBaseFee - vm.skip(true); + allo.expectCall__updateBaseFee(_baseFee); + + vm.prank(_caller); + allo.updateBaseFee(_baseFee); } - function test_UpdateTrustedForwarderRevertWhen_SenderIsNotOwner() external { + function test_UpdateTrustedForwarderRevertWhen_SenderIsNotOwner(address _caller, address _trustedForwarder) + external + { + vm.assume(_caller != address(0)); // owner is not set so is eq to 0 + // it should revert - vm.skip(true); + vm.expectRevert(Ownable.Unauthorized.selector); + + vm.prank(_caller); + allo.updateTrustedForwarder(_trustedForwarder); } - function test_UpdateTrustedForwarderWhenSenderIsOwner() external { + function test_UpdateTrustedForwarderWhenSenderIsOwner(address _caller, address _trustedForwarder) external { + vm.assume(_caller != address(0)); + + vm.prank(address(0)); + allo.transferOwnership(_caller); + + allo.mock_call__updateTrustedForwarder(_trustedForwarder); + // it should call _updateTrustedForwarder - vm.skip(true); + allo.expectCall__updateTrustedForwarder(_trustedForwarder); + + vm.prank(_caller); + allo.updateTrustedForwarder(_trustedForwarder); } - function test_AddPoolManagersGivenSenderIsAdminOfPoolId() external { + function test_AddPoolManagersGivenSenderIsAdminOfPoolId(uint256 _poolId, address[] memory _managers) external { + allo.mock_call__checkOnlyPoolAdmin(_poolId, address(this)); + for (uint256 i = 0; i < _managers.length; i++) { + allo.mock_call__addPoolManager(_poolId, _managers[i]); + } + // it should call _checkOnlyPoolAdmin + allo.expectCall__checkOnlyPoolAdmin(_poolId, address(this)); + // it should call _addPoolManager - vm.skip(true); + for (uint256 i = 0; i < _managers.length; i++) { + allo.expectCall__addPoolManager(_poolId, _managers[i]); + } + + allo.addPoolManagers(_poolId, _managers); } - function test_RemovePoolManagersGivenSenderIsAdminOfPoolId() external { + function test_RemovePoolManagersGivenSenderIsAdminOfPoolId(uint256 _poolId, address[] memory _managers) external { + allo.mock_call__checkOnlyPoolAdmin(_poolId, address(this)); + // it should call _checkOnlyPoolAdmin + allo.expectCall__checkOnlyPoolAdmin(_poolId, address(this)); + + bytes32 _role = allo.getPool(_poolId).managerRole; + // it should call _revokeRole - vm.skip(true); + for (uint256 i = 0; i < _managers.length; i++) { + allo.expectCall__revokeRole(_role, _managers[i]); + } + + allo.removePoolManagers(_poolId, _managers); } - function test_AddPoolManagersInMultiplePoolsGivenSenderIsAdminOfAllPoolIds() external { + function test_AddPoolManagersInMultiplePoolsGivenSenderIsAdminOfAllPoolIds(uint256[] memory _poolIds) external { + vm.assume(_poolIds.length > 0); + vm.assume(_poolIds.length < 100); + + address[] memory _managers = new address[](_poolIds.length); + for (uint256 i = 0; i < _poolIds.length; i++) { + allo.mock_call__checkOnlyPoolAdmin(_poolIds[i], address(this)); + _managers[i] = makeAddr((i + 1).toString()); + } + // it should call addPoolManagers - vm.skip(true); + // NOTE: we cant expect addPoolManagers because is a public function. + // instead we expect the internal that is called inside addPoolManagers + for (uint256 i = 0; i < _poolIds.length; i++) { + allo.expectCall__addPoolManager(_poolIds[i], _managers[i]); + } + + allo.addPoolManagersInMultiplePools(_poolIds, _managers); } - function test_RemovePoolManagersInMultiplePoolsGivenSenderIsAdminOfAllPoolIds() external { + function test_RemovePoolManagersInMultiplePoolsGivenSenderIsAdminOfAllPoolIds(uint256[] memory _poolIds) external { + vm.assume(_poolIds.length > 0); + vm.assume(_poolIds.length < 100); + + address[] memory _managers = new address[](_poolIds.length); + for (uint256 i = 0; i < _poolIds.length; i++) { + allo.mock_call__checkOnlyPoolAdmin(_poolIds[i], address(this)); + _managers[i] = makeAddr((i + 1).toString()); + } + // it should call removePoolManagers - vm.skip(true); + // NOTE: we cant expect removePoolManagers because is a public function. + // instead we expect the internal that is called inside removePoolManagers + for (uint256 i = 0; i < _poolIds.length; i++) { + allo.expectCall__revokeRole(allo.getPool(_poolIds[i]).managerRole, _managers[i]); + } + + allo.removePoolManagersInMultiplePools(_poolIds, _managers); } function test_RecoverFundsRevertWhen_SenderIsNotOwner() external { diff --git a/test/utils/mocks/MockAllo.sol b/test/utils/mocks/MockAllo.sol new file mode 100644 index 000000000..e912043bd --- /dev/null +++ b/test/utils/mocks/MockAllo.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.19; + +import {Allo} from "contracts/core/Allo.sol"; +import {IBaseStrategy} from "contracts/strategies/IBaseStrategy.sol"; +import {Metadata} from "contracts/core/libraries/Metadata.sol"; + +contract MockAllo is Allo { + constructor() Allo() {} + + function _initializeOwner(address newOwner) internal virtual override { + super._initializeOwner(newOwner); + } + + function _revokeRole(bytes32 role, address account) internal virtual override { + super._revokeRole(role, account); + } + + function _checkOnlyPoolManager(uint256 _poolId, address _address) internal view virtual override { + super._checkOnlyPoolManager(_poolId, _address); + } + + function _checkOnlyPoolAdmin(uint256 _poolId, address _address) internal view virtual override { + super._checkOnlyPoolAdmin(_poolId, _address); + } + + function _createPool( + address _creator, + uint256 _msgValue, + bytes32 _profileId, + IBaseStrategy _strategy, + bytes memory _initStrategyData, + address _token, + uint256 _amount, + Metadata memory _metadata, + address[] memory _managers + ) internal virtual override returns (uint256 poolId) { + return super._createPool( + _creator, _msgValue, _profileId, _strategy, _initStrategyData, _token, _amount, _metadata, _managers + ); + } + + function _allocate( + uint256 _poolId, + address[] memory _recipients, + uint256[] memory _amounts, + bytes memory _data, + uint256 _value, + address _allocator + ) internal virtual override { + super._allocate(_poolId, _recipients, _amounts, _data, _value, _allocator); + } + + function _fundPool(uint256 _amount, address _funder, uint256 _poolId, IBaseStrategy _strategy) + internal + virtual + override + { + super._fundPool(_amount, _funder, _poolId, _strategy); + } + + function _isPoolAdmin(uint256 _poolId, address _address) internal view virtual override returns (bool) { + return super._isPoolAdmin(_poolId, _address); + } + + function _isPoolManager(uint256 _poolId, address _address) internal view virtual override returns (bool) { + return super._isPoolManager(_poolId, _address); + } + + function _updateRegistry(address _registry) internal virtual override { + super._updateRegistry(_registry); + } + + function _updateTreasury(address payable _treasury) internal virtual override { + super._updateTreasury(_treasury); + } + + function _updatePercentFee(uint256 _percentFee) internal virtual override { + super._updatePercentFee(_percentFee); + } + + function _updateBaseFee(uint256 _baseFee) internal virtual override { + super._updateBaseFee(_baseFee); + } + + function _updateTrustedForwarder(address __trustedForwarder) internal virtual override { + super._updateTrustedForwarder(__trustedForwarder); + } + + function _addPoolManager(uint256 _poolId, address _manager) internal virtual override { + super._addPoolManager(_poolId, _manager); + } + + function _msgSender() internal view virtual override returns (address) { + return super._msgSender(); + } +}