From 250c1b0cc3a22d8deb8232473cfea504a87e3774 Mon Sep 17 00:00:00 2001 From: vicnaum Date: Mon, 5 Feb 2024 18:34:40 +0100 Subject: [PATCH] test: PermissionlessCreator tests done along with new TransferKeepingDelegates LensHub function. Smol bug fixed in PermissionlessCreator. --- ...sCreator.sol => PermissionlessCreator.sol} | 32 +- test/TransferKeepingDelegatesTest.t.sol | 499 ++++++++ test/misc/PermissionlessCreatorTest.t.sol | 1040 +++++++++++++++-- 3 files changed, 1493 insertions(+), 78 deletions(-) rename contracts/misc/{PermissonlessCreator.sol => PermissionlessCreator.sol} (89%) create mode 100644 test/TransferKeepingDelegatesTest.t.sol diff --git a/contracts/misc/PermissonlessCreator.sol b/contracts/misc/PermissionlessCreator.sol similarity index 89% rename from contracts/misc/PermissonlessCreator.sol rename to contracts/misc/PermissionlessCreator.sol index f8af7fa..bb765a8 100644 --- a/contracts/misc/PermissonlessCreator.sol +++ b/contracts/misc/PermissionlessCreator.sol @@ -14,7 +14,7 @@ import {ITokenHandleRegistry} from 'contracts/interfaces/ITokenHandleRegistry.so * @author Lens Protocol * @notice This is an ownable public proxy contract which is open for all. */ -contract PermissonlessCreator is Ownable { +contract PermissionlessCreator is Ownable { ILensHandles public immutable LENS_HANDLES; ITokenHandleRegistry public immutable TOKEN_HANDLE_REGISTRY; address immutable LENS_HUB; @@ -45,14 +45,22 @@ contract PermissonlessCreator is Ownable { event HandleCreationPriceChanged(uint256 newPrice); event ProfileCreationPriceChanged(uint256 newPrice); + event HandleLengthMinChanged(uint8 newMinLength); event CreditBalanceChanged(address indexed creditAddress, uint256 remainingCredits); event TrustStatusChanged(address indexed targetAddress, bool trustRevoked); + event CreditProviderStatusChanged(address indexed creditProvider, bool isCreditProvider); + + event ProfileCreatedUsingCredits(uint256 indexed profileId, address indexed creator); + event HandleCreatedUsingCredits(uint256 indexed handleId, string indexed handle, address indexed creator); constructor(address owner, address hub, address lensHandles, address tokenHandleRegistry) { _transferOwnership(owner); LENS_HUB = hub; LENS_HANDLES = ILensHandles(lensHandles); TOKEN_HANDLE_REGISTRY = ITokenHandleRegistry(tokenHandleRegistry); + emit ProfileCreationPriceChanged(_profileCreationCost); + emit HandleCreationPriceChanged(_handleCreationCost); + emit HandleLengthMinChanged(_handleLengthMin); } /////////////////////////// Permissionless payable creation functions ////////////////////////////////////////////// @@ -60,7 +68,7 @@ contract PermissonlessCreator is Ownable { function createProfile( Types.CreateProfileParams calldata createProfileParams, address[] calldata delegatedExecutors - ) external payable returns (uint256 profileId) { + ) external payable returns (uint256) { _validatePayment(_profileCreationCost); // delegatedExecutors are only allowed if to == msg.sender if (delegatedExecutors.length > 0 && createProfileParams.to != msg.sender) { @@ -81,7 +89,7 @@ contract PermissonlessCreator is Ownable { Types.CreateProfileParams calldata createProfileParams, string calldata handle, address[] calldata delegatedExecutors - ) external payable returns (uint256 profileId, uint256 handleId) { + ) external payable returns (uint256, uint256) { _validatePayment(_profileCreationCost + _handleCreationCost); if (bytes(handle).length < _handleLengthMin) { revert HandleLengthNotAllowed(); @@ -102,6 +110,7 @@ contract PermissonlessCreator is Ownable { _spendCredit(msg.sender); uint256 profileId = _createProfile(createProfileParams, delegatedExecutors); _profileCreatorUsingCredits[profileId] = msg.sender; + emit ProfileCreatedUsingCredits(profileId, msg.sender); return profileId; } @@ -120,6 +129,8 @@ contract PermissonlessCreator is Ownable { delegatedExecutors ); _profileCreatorUsingCredits[profileId] = msg.sender; + emit ProfileCreatedUsingCredits(profileId, msg.sender); + emit HandleCreatedUsingCredits(handleId, handle, msg.sender); return (profileId, handleId); } @@ -128,7 +139,9 @@ contract PermissonlessCreator is Ownable { if (bytes(handle).length < _handleLengthMin) { revert HandleLengthNotAllowed(); } - return LENS_HANDLES.mintHandle(to, handle); + uint256 handleId = LENS_HANDLES.mintHandle(to, handle); + emit HandleCreatedUsingCredits(handleId, handle, msg.sender); + return handleId; } ////////////////////////////////////////// Base functions ////////////////////////////////////////////////////////// @@ -165,7 +178,7 @@ contract PermissonlessCreator is Ownable { Types.CreateProfileParams calldata createProfileParams, string calldata handle, address[] memory delegatedExecutors - ) private returns (uint256 profileId, uint256 handleId) { + ) private returns (uint256, uint256) { // Copy the struct from calldata to memory to make it mutable Types.CreateProfileParams memory createProfileParamsMemory = createProfileParams; @@ -187,7 +200,7 @@ contract PermissonlessCreator is Ownable { // Transfer the handle & profile to the destination LENS_HANDLES.transferFrom(address(this), destination, _handleId); // keep the config if its been set - ILensHub(LENS_HUB).transferFromKeepingDelegates(address(this), destination, profileId); + ILensHub(LENS_HUB).transferFromKeepingDelegates(address(this), destination, _profileId); return (_profileId, _handleId); } @@ -255,10 +268,12 @@ contract PermissonlessCreator is Ownable { function addCreditProvider(address creditProvider) external onlyOwner { _isCreditProvider[creditProvider] = true; + emit CreditProviderStatusChanged(creditProvider, true); } function removeCreditProvider(address creditProvider) external onlyOwner { _isCreditProvider[creditProvider] = false; + emit CreditProviderStatusChanged(creditProvider, false); } function setProfileCreationPrice(uint128 newPrice) external onlyOwner { @@ -273,6 +288,7 @@ contract PermissonlessCreator is Ownable { function setHandleLengthMin(uint8 newMinLength) external onlyOwner { _handleLengthMin = newMinLength; + emit HandleLengthMinChanged(newMinLength); } function setTrustRevoked(address targetAddress, bool trustRevoked) external onlyOwner { @@ -314,4 +330,8 @@ contract PermissonlessCreator is Ownable { function getCreditBalance(address targetAddress) external view returns (uint256) { return _credits[targetAddress]; } + + function getProfileCreatorUsingCredits(uint256 profileId) external view returns (address) { + return _profileCreatorUsingCredits[profileId]; + } } diff --git a/test/TransferKeepingDelegatesTest.t.sol b/test/TransferKeepingDelegatesTest.t.sol new file mode 100644 index 0000000..3a611b7 --- /dev/null +++ b/test/TransferKeepingDelegatesTest.t.sol @@ -0,0 +1,499 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import 'test/base/BaseTest.t.sol'; +import 'test/LensBaseERC721Test.t.sol'; +import {Base64} from 'solady/utils/Base64.sol'; +import {LibString} from 'solady/utils/LibString.sol'; +import {ProfileTokenURI} from 'contracts/misc/token-uris/ProfileTokenURI.sol'; +import {IProfileTokenURI} from 'contracts/interfaces/IProfileTokenURI.sol'; +import {ILensProfiles} from 'contracts/interfaces/ILensProfiles.sol'; +import {MockTokenHolderContract} from 'test/mocks/MockTokenHolderContract.sol'; +import {Address} from '@openzeppelin/contracts/utils/Address.sol'; + +interface IGuardedToken is IERC721 { + function DANGER__disableTokenGuardian() external; + + function enableTokenGuardian() external; + + function getTokenGuardianDisablingTimestamp(address wallet) external view returns (uint256); + + function transferFromKeepingDelegates(address from, address to, uint256 tokenId) external; + + function burn(uint256 tokenId) external; +} + +contract TransferKeepingDelegatesTest is BaseTest { + using Address for address; + + function _getERC721TokenAddress() internal view virtual returns (address) { + return address(hub); + } + + function _LensProfiles() private view returns (ILensProfiles) { + return ILensProfiles(_getERC721TokenAddress()); + } + + function _mintERC721(address to) internal virtual returns (uint256) { + vm.assume(!_isLensHubProxyAdmin(to)); + return _createProfile(to); + } + + function _burnERC721(uint256 tokenId) internal virtual { + return hub.burn(tokenId); + } + + function _disableGuardian(address wallet) internal { + _effectivelyDisableProfileGuardian(wallet); + } + + function _assumeNotProxyAdmin(address account) internal view virtual { + vm.assume(!_isLensHubProxyAdmin(account)); + } + + function _TOKEN_GUARDIAN_COOLDOWN() internal view returns (uint256) { + return fork ? hub.TOKEN_GUARDIAN_COOLDOWN() : PROFILE_GUARDIAN_COOLDOWN; + } + + function _guardedToken() private view returns (IGuardedToken) { + return IGuardedToken(_getERC721TokenAddress()); + } + + MockTokenHolderContract tokenHolderContract; + uint256 tokenIdHeldByEOA; + uint256 tokenIdHeldByNonEOA; + + function setUp() public virtual override { + super.setUp(); + tokenHolderContract = new MockTokenHolderContract(); + tokenHolderContract.setCollection(address(_guardedToken())); + tokenIdHeldByEOA = _mintERC721(defaultAccount.owner); + tokenIdHeldByNonEOA = _mintERC721(address(this)); + _guardedToken().safeTransferFrom(address(this), address(tokenHolderContract), tokenIdHeldByNonEOA); + } + + // TokenGuardian tests + + function testCannot_transferFrom_ifEOA_andTokenGuardianEnabled(address to) public { + vm.assume(to != address(0)); + + vm.prank(governance); + hub.whitelistProfileCreator(defaultAccount.owner, true); + + vm.prank(defaultAccount.owner); + vm.expectRevert(Errors.GuardianEnabled.selector); + _guardedToken().transferFromKeepingDelegates(defaultAccount.owner, to, tokenIdHeldByEOA); + assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), defaultAccount.owner); + } + + function testCannot_transferFrom_ifEOA_andTokenGuardianDisabled_butNotTakenEffectYet( + uint256 elapsedTimeAfterDisabling, + address to + ) public { + vm.assume(to != address(0)); + elapsedTimeAfterDisabling = bound(elapsedTimeAfterDisabling, 0, _TOKEN_GUARDIAN_COOLDOWN() - 1); + vm.prank(defaultAccount.owner); + _guardedToken().DANGER__disableTokenGuardian(); + + vm.warp(block.timestamp + elapsedTimeAfterDisabling); + + vm.prank(governance); + hub.whitelistProfileCreator(defaultAccount.owner, true); + + vm.prank(defaultAccount.owner); + vm.expectRevert(Errors.GuardianEnabled.selector); + _guardedToken().transferFromKeepingDelegates(defaultAccount.owner, to, tokenIdHeldByEOA); + assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), defaultAccount.owner); + } + + function testTransferFrom_ifEOA_onlyAfterTokenGuardianIsEffectivelyDisabled(address to) public { + vm.assume(to != address(0)); + _effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner); + + vm.prank(governance); + hub.whitelistProfileCreator(defaultAccount.owner, true); + + vm.prank(defaultAccount.owner); + _guardedToken().transferFromKeepingDelegates(defaultAccount.owner, to, tokenIdHeldByEOA); + + assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), to); + } + + function testApprovalStateDoesNotChange_afterProtectionStateChanges(address anotherAddress) public { + vm.assume(anotherAddress != address(0)); + vm.assume(anotherAddress != defaultAccount.owner); + + // Disable protection + _effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner); + + // Approve + vm.prank(defaultAccount.owner); + _guardedToken().approve(anotherAddress, tokenIdHeldByEOA); + + // Approve state has changed + assertEq(_guardedToken().getApproved(tokenIdHeldByEOA), anotherAddress); + + // Enable protection + vm.prank(defaultAccount.owner); + _guardedToken().enableTokenGuardian(); + + // Approve state remains the same after enabling protection + assertEq(_guardedToken().getApproved(tokenIdHeldByEOA), anotherAddress); + + vm.prank(governance); + hub.whitelistProfileCreator(defaultAccount.owner, true); + + // But, you cannot transfer even if approved, because the protection is enabled + vm.prank(defaultAccount.owner); + vm.expectRevert(Errors.GuardianEnabled.selector); + _guardedToken().transferFromKeepingDelegates(defaultAccount.owner, anotherAddress, tokenIdHeldByEOA); + } + + function testApproveForAllState_DoesNotChange_AfterGuardianStateChanges(address anotherAddress) public { + vm.assume(anotherAddress != address(0)); + vm.assume(anotherAddress != defaultAccount.owner); + + // Disable protection + _effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner); + + // ApproveForAll + vm.prank(defaultAccount.owner); + _guardedToken().setApprovalForAll(anotherAddress, true); + + // ApproveForAll state has changed + assertTrue(_guardedToken().isApprovedForAll(defaultAccount.owner, anotherAddress)); + + // Enable protection + vm.prank(defaultAccount.owner); + _guardedToken().enableTokenGuardian(); + + // ApproveForAll state remains the same after enabling protection + assertTrue(_guardedToken().isApprovedForAll(defaultAccount.owner, anotherAddress)); + + vm.prank(governance); + hub.whitelistProfileCreator(defaultAccount.owner, true); + + // But, you cannot transfer even if ApprovedForAll, because the protection is enabled + vm.prank(defaultAccount.owner); + vm.expectRevert(Errors.GuardianEnabled.selector); + _guardedToken().transferFromKeepingDelegates(defaultAccount.owner, anotherAddress, tokenIdHeldByEOA); + } + + function testTransfersDoesNotAffectProtectionState_InboundTransfer(address anotherAddress) public { + vm.assume(anotherAddress != address(0)); + vm.assume(anotherAddress != defaultAccount.owner); + vm.assume(anotherAddress.code.length == 0); + + // UserTwo does not have any profile + vm.assume(_guardedToken().balanceOf(anotherAddress) == 0); + + // User disables protection, so it can perform a transfer later + _effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner); + + // UserTwo disables protection + _effectivelyDisableGuardian(address(_guardedToken()), anotherAddress); + + // UserTwo ApproveForAll User + vm.prank(anotherAddress); + _guardedToken().setApprovalForAll(defaultAccount.owner, true); + + vm.prank(governance); + hub.whitelistProfileCreator(defaultAccount.owner, true); + + // UserTwo receives a profile from User + vm.prank(defaultAccount.owner); + _guardedToken().transferFromKeepingDelegates(defaultAccount.owner, anotherAddress, tokenIdHeldByEOA); + + // UserTwo now holds the profile + assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), anotherAddress); + + vm.prank(governance); + hub.whitelistProfileCreator(anotherAddress, true); + + // The profile is unprotected, and User is ApproveForAll by UserTwo, so can transfer the profile back + vm.prank(anotherAddress); + _guardedToken().transferFromKeepingDelegates(anotherAddress, defaultAccount.owner, tokenIdHeldByEOA); + + assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), defaultAccount.owner); + } + + function testTransfersDoNotAffectProtectionState_OutboundTransfer(address anotherAddress) public { + vm.assume(anotherAddress != address(0)); + vm.assume(anotherAddress != defaultAccount.owner); + + // Disables protection + _effectivelyDisableGuardian(address(_guardedToken()), defaultAccount.owner); + + vm.prank(governance); + hub.whitelistProfileCreator(defaultAccount.owner, true); + + // Transfers the profile to UserTwo + vm.prank(defaultAccount.owner); + _guardedToken().transferFromKeepingDelegates(defaultAccount.owner, anotherAddress, tokenIdHeldByEOA); + + // User does not have the profile anymore + assertEq(_guardedToken().ownerOf(tokenIdHeldByEOA), anotherAddress); + + // Transfers does not affect protection state, so User can execute ApproveForAll even after transfer + vm.prank(defaultAccount.owner); + _guardedToken().setApprovalForAll(anotherAddress, true); + assertTrue(_guardedToken().isApprovedForAll(defaultAccount.owner, anotherAddress)); + } + + // General ERC721.TransferFrom tests + + function testTransferFromKeepingDelegates_SenderIsApproved(address owner, address approvedTo, address to) public { + vm.assume(owner != address(0)); + vm.assume(approvedTo != address(0)); + vm.assume(owner != approvedTo); + vm.assume(to != address(0)); + + uint256 tokenId = _mintERC721(owner); + + _disableGuardian(owner); + + vm.prank(owner); + _LensProfiles().approve(approvedTo, tokenId); + + uint256 ownerBalanceBefore = _LensProfiles().balanceOf(owner); + uint256 toBalanceBefore = _LensProfiles().balanceOf(to); + + _assumeNotProxyAdmin(approvedTo); + + _disableGuardian(approvedTo); + + vm.prank(governance); + hub.whitelistProfileCreator(approvedTo, true); + + vm.prank(approvedTo); + _LensProfiles().transferFromKeepingDelegates(owner, to, tokenId); + + uint256 ownerBalanceAfter = _LensProfiles().balanceOf(owner); + uint256 toBalanceAfter = _LensProfiles().balanceOf(to); + + assertEq(_LensProfiles().getApproved(tokenId), address(0)); + assertEq(_LensProfiles().ownerOf(tokenId), to); + + if (owner != to) { + assertEq(ownerBalanceAfter, ownerBalanceBefore - 1); + assertEq(toBalanceAfter, toBalanceBefore + 1); + } + } + + function testTransferFromKeepingDelegates_SenderIsTheOwner(address owner, address to) public { + vm.assume(owner != address(0)); + vm.assume(to != address(0)); + + uint256 tokenId = _mintERC721(owner); + + uint256 ownerBalanceBefore = _LensProfiles().balanceOf(owner); + uint256 toBalanceBefore = _LensProfiles().balanceOf(to); + + _disableGuardian(owner); + + vm.prank(governance); + hub.whitelistProfileCreator(owner, true); + + vm.prank(owner); + _LensProfiles().transferFromKeepingDelegates(owner, to, tokenId); + + uint256 ownerBalanceAfter = _LensProfiles().balanceOf(owner); + uint256 toBalanceAfter = _LensProfiles().balanceOf(to); + + assertEq(_LensProfiles().getApproved(tokenId), address(0)); + assertEq(_LensProfiles().ownerOf(tokenId), to); + + if (owner != to) { + assertEq(ownerBalanceAfter, ownerBalanceBefore - 1); + assertEq(toBalanceAfter, toBalanceBefore + 1); + } + } + + function testTransferFromKeepingDelegates_SenderIsApprovedForAll( + address owner, + address approvedTo, + address to + ) public { + vm.assume(owner != address(0)); + vm.assume(approvedTo != address(0)); + vm.assume(owner != approvedTo); + vm.assume(to != address(0)); + + uint256 tokenId = _mintERC721(owner); + + _disableGuardian(owner); + + vm.prank(owner); + _LensProfiles().setApprovalForAll(approvedTo, true); + + uint256 ownerBalanceBefore = _LensProfiles().balanceOf(owner); + uint256 toBalanceBefore = _LensProfiles().balanceOf(to); + + _assumeNotProxyAdmin(approvedTo); + + _disableGuardian(approvedTo); + + vm.prank(governance); + hub.whitelistProfileCreator(approvedTo, true); + + vm.prank(approvedTo); + _LensProfiles().transferFromKeepingDelegates(owner, to, tokenId); + + uint256 ownerBalanceAfter = _LensProfiles().balanceOf(owner); + uint256 toBalanceAfter = _LensProfiles().balanceOf(to); + + assertEq(_LensProfiles().ownerOf(tokenId), to); + + if (owner != to) { + assertEq(ownerBalanceAfter, ownerBalanceBefore - 1); + assertEq(toBalanceAfter, toBalanceBefore + 1); + } + } + + function testCannot_TransferFromKeepingDelegates_NotOwner(address owner, address to, address otherAddress) public { + vm.assume(owner != to); + vm.assume(owner != otherAddress); + vm.assume(to != address(0)); + vm.assume(owner != address(0)); + vm.assume(otherAddress != address(0)); + + uint256 tokenId = _mintERC721(owner); + + _assumeNotProxyAdmin(otherAddress); + + vm.prank(governance); + hub.whitelistProfileCreator(otherAddress, true); + + vm.expectRevert(Errors.NotOwnerOrApproved.selector); + vm.prank(otherAddress); + _LensProfiles().transferFromKeepingDelegates(owner, to, tokenId); + } + + function testCannotTransferFromKeepingDelegates_WrongFromParameter_SenderOwner( + address owner, + address from, + address to + ) public { + _assumeNotProxyAdmin(owner); + vm.assume(owner != to); + vm.assume(owner != from); + vm.assume(owner != address(0)); + vm.assume(to != address(0)); + + uint256 tokenId = _mintERC721(owner); + + vm.prank(governance); + hub.whitelistProfileCreator(owner, true); + + vm.expectRevert(Errors.InvalidOwner.selector); + + vm.prank(owner); + _LensProfiles().transferFromKeepingDelegates(from, to, tokenId); + } + + function testCannot_TransferFromKeepingDelegates_NonexistingToken( + uint256 tokenId, + address from, + address to + ) public { + vm.assume(from != address(0)); + vm.assume(to != address(0)); + + vm.assume(_LensProfiles().exists(tokenId) == false); + + vm.prank(governance); + hub.whitelistProfileCreator(address(this), true); + + vm.expectRevert(Errors.TokenDoesNotExist.selector); + _LensProfiles().transferFromKeepingDelegates(from, to, tokenId); + } + + function testCannot_TransferFromKeepingDelegates_ToZero(address owner) public { + vm.assume(owner != address(0)); + uint256 tokenId = _mintERC721(owner); + + vm.prank(governance); + hub.whitelistProfileCreator(owner, true); + + vm.expectRevert(Errors.InvalidParameter.selector); + + vm.prank(owner); + _LensProfiles().transferFromKeepingDelegates(owner, address(0), tokenId); + } + + // Tests list for TransferFromKeepingDelegates function: + + // Negatives + + function testCannot_TransferFromKeepingDelegates_IfNotWhitelistedProfileCreator( + address owner, + address to, + address approvedTo + ) public { + vm.assume(owner != to); + vm.assume(to != address(0)); + vm.assume(owner != address(0)); + vm.assume(approvedTo != address(0)); + vm.assume(hub.isProfileCreatorWhitelisted(approvedTo) == false); + _assumeNotProxyAdmin(approvedTo); + _assumeNotProxyAdmin(owner); + + _disableGuardian(owner); + + uint256 tokenId = _mintERC721(owner); + + if (owner != approvedTo) { + vm.prank(owner); + _LensProfiles().approve(approvedTo, tokenId); + } + + vm.expectRevert(Errors.NotAllowed.selector); + vm.prank(approvedTo); + _LensProfiles().transferFromKeepingDelegates(owner, to, tokenId); + } + + // Scenarios + + function testTransferFromKeepingDelegates(address owner, address to, address approvedTo) public { + vm.assume(owner != to); + vm.assume(to != address(0)); + vm.assume(owner != address(0)); + vm.assume(approvedTo != address(0)); + _assumeNotProxyAdmin(approvedTo); + + _disableGuardian(owner); + + uint256 tokenId = _mintERC721(owner); + + vm.prank(governance); + hub.whitelistProfileCreator(approvedTo, true); + + if (owner != approvedTo) { + vm.prank(owner); + _LensProfiles().approve(approvedTo, tokenId); + } + + address[] memory delegatedExecutors = new address[](3); + delegatedExecutors[0] = makeAddr('DE0'); + delegatedExecutors[1] = makeAddr('DE1'); + delegatedExecutors[2] = makeAddr('DE2'); + + // Initialize an array of bools with the same length as delegatedExecutors + bool[] memory executorEnabled = new bool[](delegatedExecutors.length); + + // Fill the array with `true` + for (uint256 i = 0; i < delegatedExecutors.length; i++) { + executorEnabled[i] = true; + } + + vm.prank(owner); + hub.changeDelegatedExecutorsConfig(tokenId, delegatedExecutors, executorEnabled); + + vm.prank(approvedTo); + _LensProfiles().transferFromKeepingDelegates(owner, to, tokenId); + + for (uint256 i = 0; i < delegatedExecutors.length; i++) { + assertTrue(hub.isDelegatedExecutorApproved(tokenId, delegatedExecutors[i])); + } + } +} diff --git a/test/misc/PermissionlessCreatorTest.t.sol b/test/misc/PermissionlessCreatorTest.t.sol index 3636b2e..99f1a94 100644 --- a/test/misc/PermissionlessCreatorTest.t.sol +++ b/test/misc/PermissionlessCreatorTest.t.sol @@ -1,110 +1,1006 @@ -/* - List of tests for PermissionlessCreator contract: +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.13; + +import 'test/base/BaseTest.t.sol'; +import {PermissionlessCreator} from 'contracts/misc/PermissionlessCreator.sol'; +import {Types} from 'contracts/libraries/constants/Types.sol'; + +abstract contract PermissionlessCreatorTestBase is BaseTest { + using stdJson for string; + + PermissionlessCreator permissionlessCreator; + address permissionlessCreatorOwner = makeAddr('PERMISSIONLESS_CREATOR_OWNER'); + + function setUp() public virtual override { + super.setUp(); + + if (fork) { + if (keyExists(json, string(abi.encodePacked('.', forkEnv, '.PermissionlessCreator')))) { + permissionlessCreator = PermissionlessCreator( + json.readAddress(string(abi.encodePacked('.', forkEnv, '.PermissionlessCreator'))) + ); + permissionlessCreatorOwner = permissionlessCreator.owner(); + } else { + console.log('PermissionlessCreator key does not exist'); + if (forkVersion == 1) { + console.log('No PermissionlessCreator address found - deploying new one'); + permissionlessCreator = new PermissionlessCreator( + permissionlessCreatorOwner, + address(hub), + address(lensHandles), + address(tokenHandleRegistry) + ); + } else { + console.log('No PermissionlessCreator address found in addressBook, which is required for V2'); + revert('No PermissionlessCreator address found in addressBook, which is required for V2'); + } + } + } else { + permissionlessCreator = new PermissionlessCreator( + permissionlessCreatorOwner, + address(hub), + address(lensHandles), + address(tokenHandleRegistry) + ); + } + + vm.prank(governance); + hub.whitelistProfileCreator(address(permissionlessCreator), true); + } +} + +contract PermissionlessCreatorTest_PaidCreation is PermissionlessCreatorTestBase { + function setUp() public override { + super.setUp(); + vm.deal( + address(this), + (permissionlessCreator.getProfileCreationPrice() + permissionlessCreator.getHandleCreationPrice()) * 10 + ); + } // Paid creation - // Negatives + // Negatives - // Payment negatives - testCannot_CreateProfile_IfNotEnoughPayment - testCannot_CreateHandle_IfNotEnoughPayment - testCannot_CreateProfileWithHandle_IfNotEnoughPayment + // Payment negatives + function testCannot_CreateProfile_IfNotEnoughPayment(uint256 amount) public { + amount = bound(amount, 0, permissionlessCreator.getProfileCreationPrice() - 1); - // DelegatedExecutors negatives - testCannot_CreateProfile_WithDE_IfNotToHimself - testCannot_CreateHandle_WithDE_IfNotToHimself - testCannot_CreateProfileWithHandle_WithDE_IfNotToHimself + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: address(this), + followModule: address(0), + followModuleInitData: '' + }); - // Handle Length negatives - testCannot_CreateHandle_IfHandleLengthIsLessThanMin - testCannot_CreateProfileWithHandle_IfHandleLengthIsLessThanMin + address[] memory delegates = new address[](0); - // Scenarios + vm.expectRevert(PermissionlessCreator.InvalidFunds.selector); + permissionlessCreator.createProfile{value: amount}(createProfileParams, delegates); + } - testCreateProfile_WithDE - testCreateProfile_WithoutDE - testCreateHandle - testCreateProfileWithHandle_WithDE - testCreateProfileWithHandle_WithoutDE + function testCannot_CreateHandle_IfNotEnoughPayment(uint256 amount) public { + string memory handle = 'testhandle_39453226'; + address to = address(this); + + amount = bound(amount, 0, permissionlessCreator.getHandleCreationPrice() - 1); + + vm.expectRevert(PermissionlessCreator.InvalidFunds.selector); + permissionlessCreator.createHandle{value: amount}(to, handle); + } + + function testCannot_CreateProfileWithHandle_IfNotEnoughPayment(uint256 amount) public { + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: address(this), + followModule: address(0), + followModuleInitData: '' + }); + + string memory handle = 'testhandle_39453226'; + address[] memory delegates = new address[](0); + + amount = bound(amount, 0, permissionlessCreator.getProfileCreationPrice() - 1); + + vm.expectRevert(PermissionlessCreator.InvalidFunds.selector); + permissionlessCreator.createProfileWithHandle{value: amount}(createProfileParams, handle, delegates); + } + + // DelegatedExecutors negatives + function testCannot_CreateProfile_WithDE_IfNotToHimself(address to) public { + vm.assume(to != address(this)); + vm.assume(to != address(0)); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + uint256 amount = permissionlessCreator.getProfileCreationPrice(); + + vm.expectRevert(PermissionlessCreator.NotAllowed.selector); + + permissionlessCreator.createProfile{value: amount}(createProfileParams, delegates); + } + + function testCannot_CreateProfileWithHandle_WithDE_IfNotToHimself(address to) public { + vm.assume(to != address(this)); + vm.assume(to != address(0)); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + string memory handle = 'testhandle_39453226'; + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + uint256 amount = permissionlessCreator.getProfileCreationPrice() + + permissionlessCreator.getHandleCreationPrice(); + + vm.expectRevert(PermissionlessCreator.NotAllowed.selector); + + permissionlessCreator.createProfileWithHandle{value: amount}(createProfileParams, handle, delegates); + } + + // Handle Length negatives + function testCannot_CreateHandle_IfHandleLengthIsLessThanMin() public { + string memory handleTemplate = 'testin12345678901234567890'; + + string memory handle = LibString.slice(handleTemplate, 0, permissionlessCreator.getHandleLengthMin() - 1); + + uint256 amount = permissionlessCreator.getHandleCreationPrice(); + + vm.expectRevert(PermissionlessCreator.HandleLengthNotAllowed.selector); + + permissionlessCreator.createHandle{value: amount}(address(this), handle); + } + + function testCannot_CreateProfileWithHandle_IfHandleLengthIsLessThanMin() public { + string memory handleTemplate = 'testin12345678901234567890'; + + string memory handle = LibString.slice(handleTemplate, 0, permissionlessCreator.getHandleLengthMin() - 1); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: address(this), + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](0); + + uint256 amount = permissionlessCreator.getProfileCreationPrice() + + permissionlessCreator.getHandleCreationPrice(); + + vm.expectRevert(PermissionlessCreator.HandleLengthNotAllowed.selector); + + permissionlessCreator.createProfileWithHandle{value: amount}(createProfileParams, handle, delegates); + } + + // Scenarios + + function testCreateProfile_WithDE(address owner) public { + vm.assume(owner != address(0)); + + vm.deal( + owner, + (permissionlessCreator.getProfileCreationPrice() + permissionlessCreator.getHandleCreationPrice()) * 10 + ); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: owner, + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + uint256 amount = permissionlessCreator.getProfileCreationPrice(); + + vm.prank(owner); + uint256 profileId = permissionlessCreator.createProfile{value: amount}(createProfileParams, delegates); + + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(hub.ownerOf(profileId), owner); + } + + function testCreateProfile_WithoutDE(address owner, address to) public { + vm.assume(owner != address(0)); + vm.assume(to != address(0)); + + vm.deal( + owner, + (permissionlessCreator.getProfileCreationPrice() + permissionlessCreator.getHandleCreationPrice()) * 10 + ); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](0); + + uint256 amount = permissionlessCreator.getProfileCreationPrice(); + + vm.prank(owner); + uint256 profileId = permissionlessCreator.createProfile{value: amount}(createProfileParams, delegates); + + assertEq(hub.ownerOf(profileId), to); + } + + function testCreateHandle(address to) public { + vm.assume(to != address(0)); + + string memory handle = 'testhandle_39453226'; + + uint256 amount = permissionlessCreator.getHandleCreationPrice(); + + uint256 handleId = permissionlessCreator.createHandle{value: amount}(to, handle); + + assertEq(lensHandles.ownerOf(handleId), to); + } + + function testCreateProfileWithHandle_WithDE(address owner) public { + vm.assume(owner != address(0)); + + vm.deal( + owner, + (permissionlessCreator.getProfileCreationPrice() + permissionlessCreator.getHandleCreationPrice()) * 10 + ); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: owner, + followModule: address(0), + followModuleInitData: '' + }); + + string memory handle = 'testhandle_39453226'; + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + uint256 amount = permissionlessCreator.getProfileCreationPrice() + + permissionlessCreator.getHandleCreationPrice(); + + vm.prank(owner); + (uint256 profileId, uint256 handleId) = permissionlessCreator.createProfileWithHandle{value: amount}( + createProfileParams, + handle, + delegates + ); + + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(hub.ownerOf(profileId), owner); + assertEq(lensHandles.ownerOf(handleId), owner); + } + + function testCreateProfileWithHandle_WithoutDE(address owner, address to) public { + vm.assume(owner != address(0)); + vm.assume(to != address(0)); + + vm.deal( + owner, + (permissionlessCreator.getProfileCreationPrice() + permissionlessCreator.getHandleCreationPrice()) * 10 + ); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + string memory handle = 'testhandle_39453226'; + address[] memory delegates = new address[](0); + + uint256 amount = permissionlessCreator.getProfileCreationPrice() + + permissionlessCreator.getHandleCreationPrice(); + + vm.prank(owner); + (uint256 profileId, uint256 handleId) = permissionlessCreator.createProfileWithHandle{value: amount}( + createProfileParams, + handle, + delegates + ); + + assertEq(hub.ownerOf(profileId), to); + assertEq(lensHandles.ownerOf(handleId), to); + } +} + +contract PermissionlessCreatorTest_Credits is PermissionlessCreatorTestBase { + address creditProvider = makeAddr('CREDIT_PROVIDER'); + address approvedProfileCreator = makeAddr('CREDIT_CREATOR'); + + function setUp() public override { + super.setUp(); + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.addCreditProvider(creditProvider); + + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(approvedProfileCreator, 1); + + _effectivelyDisableProfileGuardian(approvedProfileCreator); + } // Creation with credits - // Negatives + // Negatives - // Credits negatives - testCannot_CreateProfileUsingCredits_IfNotEnoughCredits - testCannot_CreateHandleUsingCredits_IfNotEnoughCredits - testCannot_CreateProfileWithHandleUsingCredits_IfNotEnoughCredits + // Credits negatives + function testCannot_CreateProfileUsingCredits_IfNotEnoughCredits(address creator, address to) public { + vm.assume(to != address(0)); + vm.assume(creator != address(0)); + vm.assume(permissionlessCreator.getCreditBalance(creator) < 1); - // TrustRevoked negatives - testCannot_CreateProfileUsingCredits_IfTrustRevoked - testCannot_CreateHandleUsingCredits_IfTrustRevoked - testCannot_CreateProfileWithHandleUsingCredits_IfTrustRevoked + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); - // Handle Length negatives - testCannot_CreateHandleUsingCredits_IfHandleLengthIsLessThanMin - testCannot_CreateProfileWithHandleUsingCredits_IfHandleLengthIsLessThanMin + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); - // Scenarios + vm.expectRevert(stdError.arithmeticError); + vm.prank(creator); + permissionlessCreator.createProfileUsingCredits(createProfileParams, delegates); + } - testCreateProfileUsingCredits - testCreateHandleUsingCredits - testCreateProfileWithHandleUsingCredits + function testCannot_CreateHandleUsingCredits_IfNotEnoughCredits(address creator, address to) public { + vm.assume(to != address(0)); + vm.assume(creator != address(0)); + vm.assume(permissionlessCreator.getCreditBalance(creator) < 1); + + string memory handle = 'testhandle_39453226'; + + vm.expectRevert(stdError.arithmeticError); + vm.prank(creator); + permissionlessCreator.createHandleUsingCredits(to, handle); + } + + function testCannot_CreateProfileWithHandleUsingCredits_IfNotEnoughCredits(address creator, address to) public { + vm.assume(to != address(0)); + vm.assume(creator != address(0)); + vm.assume(permissionlessCreator.getCreditBalance(creator) < 1); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + string memory handle = 'testhandle_39453226'; + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + vm.expectRevert(stdError.arithmeticError); + vm.prank(creator); + permissionlessCreator.createProfileWithHandleUsingCredits(createProfileParams, handle, delegates); + } + + // TrustRevoked negatives + // testCannot_CreateProfileUsingCredits_IfTrustRevoked + // testCannot_CreateHandleUsingCredits_IfTrustRevoked + // testCannot_CreateProfileWithHandleUsingCredits_IfTrustRevoked + + // Handle Length negatives + function testCannot_CreateHandleUsingCredits_IfHandleLengthIsLessThanMin() public { + string memory handleTemplate = 'testin12345678901234567890'; + + string memory handle = LibString.slice(handleTemplate, 0, permissionlessCreator.getHandleLengthMin() - 1); + + vm.expectRevert(PermissionlessCreator.HandleLengthNotAllowed.selector); + vm.prank(approvedProfileCreator); + permissionlessCreator.createHandleUsingCredits(address(this), handle); + } + + function testCannot_CreateProfileWithHandleUsingCredits_IfHandleLengthIsLessThanMin() public { + string memory handleTemplate = 'testin12345678901234567890'; + + string memory handle = LibString.slice(handleTemplate, 0, permissionlessCreator.getHandleLengthMin() - 1); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: address(this), + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](0); + + vm.expectRevert(PermissionlessCreator.HandleLengthNotAllowed.selector); + vm.prank(approvedProfileCreator); + permissionlessCreator.createProfileWithHandleUsingCredits(createProfileParams, handle, delegates); + } + + // Scenarios + + function testCreateProfileUsingCredits(address to) public { + vm.assume(to != address(0)); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + uint256 creatorCreditsBefore = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + vm.prank(approvedProfileCreator); + uint256 profileId = permissionlessCreator.createProfileUsingCredits(createProfileParams, delegates); + + uint256 creatorCreditsAfter = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + assertEq(creatorCreditsBefore - 1, creatorCreditsAfter); + assertEq(hub.ownerOf(profileId), to); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(permissionlessCreator.getProfileCreatorUsingCredits(profileId), approvedProfileCreator); + } + + function testCreateHandleUsingCredits(address to) public { + vm.assume(to != address(0)); + + string memory handle = 'testhandle_39453226'; + + uint256 creatorCreditsBefore = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + vm.prank(approvedProfileCreator); + uint256 handleId = permissionlessCreator.createHandleUsingCredits(to, handle); + + uint256 creatorCreditsAfter = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + assertEq(creatorCreditsBefore - 1, creatorCreditsAfter); + assertEq(lensHandles.ownerOf(handleId), to); + } + + function testCreateProfileWithHandleUsingCredits(address to) public { + vm.assume(to != address(0)); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + string memory handle = 'testhandle_39453226'; + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + uint256 creatorCreditsBefore = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + vm.prank(approvedProfileCreator); + (uint256 profileId, uint256 handleId) = permissionlessCreator.createProfileWithHandleUsingCredits( + createProfileParams, + handle, + delegates + ); + + uint256 creatorCreditsAfter = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + assertEq(creatorCreditsBefore - 1, creatorCreditsAfter); + assertEq(hub.ownerOf(profileId), to); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(permissionlessCreator.getProfileCreatorUsingCredits(profileId), approvedProfileCreator); + assertEq(lensHandles.ownerOf(handleId), to); + } + + // Trust Revoking + + function testCannot_CreateProfileUsingTokens_IfTrustRevoked(address to) public { + vm.assume(to != address(0)); + + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(approvedProfileCreator, 10); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + uint256 creatorCreditsBefore = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + vm.prank(approvedProfileCreator); + uint256 profileId = permissionlessCreator.createProfileUsingCredits(createProfileParams, delegates); + + uint256 creatorCreditsAfter = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + assertEq(creatorCreditsBefore - 1, creatorCreditsAfter); + assertEq(hub.ownerOf(profileId), to); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(permissionlessCreator.getProfileCreatorUsingCredits(profileId), approvedProfileCreator); + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setTrustRevoked(approvedProfileCreator, true); + + assertEq(permissionlessCreator.isTrustRevoked(approvedProfileCreator), true); + assertEq(permissionlessCreator.getCreditBalance(approvedProfileCreator), 0); + + vm.expectRevert(stdError.arithmeticError); + vm.prank(approvedProfileCreator); + permissionlessCreator.createProfileUsingCredits(createProfileParams, delegates); + } + + function testCannot_CreateHandleUsingTokens_IfTrustRevoked(address to) public { + vm.assume(to != address(0)); + + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(approvedProfileCreator, 10); + + string memory handle = 'testhandle_39453226'; + + uint256 creatorCreditsBefore = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + vm.prank(approvedProfileCreator); + uint256 handleId = permissionlessCreator.createHandleUsingCredits(to, handle); + + uint256 creatorCreditsAfter = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + assertEq(creatorCreditsBefore - 1, creatorCreditsAfter); + assertEq(lensHandles.ownerOf(handleId), to); + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setTrustRevoked(approvedProfileCreator, true); + + assertEq(permissionlessCreator.isTrustRevoked(approvedProfileCreator), true); + assertEq(permissionlessCreator.getCreditBalance(approvedProfileCreator), 0); + + vm.expectRevert(stdError.arithmeticError); + vm.prank(approvedProfileCreator); + permissionlessCreator.createHandleUsingCredits(to, handle); + } + + function testCannot_CreateProfileWithHandleUsingTokens_IfTrustRevoked(address to) public { + vm.assume(to != address(0)); + + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(approvedProfileCreator, 10); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: to, + followModule: address(0), + followModuleInitData: '' + }); + + string memory handle = 'testhandle_39453226'; + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + uint256 creatorCreditsBefore = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + vm.prank(approvedProfileCreator); + (uint256 profileId, uint256 handleId) = permissionlessCreator.createProfileWithHandleUsingCredits( + createProfileParams, + handle, + delegates + ); + + uint256 creatorCreditsAfter = permissionlessCreator.getCreditBalance(approvedProfileCreator); + + assertEq(creatorCreditsBefore - 1, creatorCreditsAfter); + assertEq(hub.ownerOf(profileId), to); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(permissionlessCreator.getProfileCreatorUsingCredits(profileId), approvedProfileCreator); + assertEq(lensHandles.ownerOf(handleId), to); + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setTrustRevoked(approvedProfileCreator, true); + + assertEq(permissionlessCreator.isTrustRevoked(approvedProfileCreator), true); + assertEq(permissionlessCreator.getCreditBalance(approvedProfileCreator), 0); + + vm.expectRevert(stdError.arithmeticError); + vm.prank(approvedProfileCreator); + permissionlessCreator.createProfileWithHandleUsingCredits(createProfileParams, handle, delegates); + } // TransferFromKeepingDelegates helper function - // Negatives + // Negatives - testCannot_TransferFromKeepingDelegates_IfTrustRevoked - testCannot_TransferFromKeepingDelegates_IfWasNotCreator + function testCannot_TransferFromKeepingDelegates_IfTrustRevoked(address to) public { + vm.assume(to != address(0)); - // Scenarios + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: approvedProfileCreator, + followModule: address(0), + followModuleInitData: '' + }); - testTransferFromKeepingDelegates + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + vm.prank(approvedProfileCreator); + uint256 profileId = permissionlessCreator.createProfileUsingCredits(createProfileParams, delegates); + + assertEq(hub.ownerOf(profileId), approvedProfileCreator); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(permissionlessCreator.getProfileCreatorUsingCredits(profileId), approvedProfileCreator); + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setTrustRevoked(approvedProfileCreator, true); + + vm.startPrank(approvedProfileCreator); + hub.approve(address(permissionlessCreator), profileId); + vm.expectRevert(PermissionlessCreator.NotAllowed.selector); + permissionlessCreator.transferFromKeepingDelegates(approvedProfileCreator, to, profileId); + vm.stopPrank(); + } + + function testCannot_TransferFromKeepingDelegates_IfWasNotCreator(address to, address owner) public { + vm.assume(to != address(0)); + vm.assume(owner != address(0)); + vm.assume(owner != approvedProfileCreator); + + _effectivelyDisableProfileGuardian(owner); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: owner, + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + vm.prank(approvedProfileCreator); + uint256 profileId = permissionlessCreator.createProfileUsingCredits(createProfileParams, delegates); + + assertEq(hub.ownerOf(profileId), owner); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(permissionlessCreator.getProfileCreatorUsingCredits(profileId), approvedProfileCreator); + + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(owner, 10); + + vm.startPrank(owner); + hub.approve(address(permissionlessCreator), profileId); + vm.expectRevert(PermissionlessCreator.NotAllowed.selector); + permissionlessCreator.transferFromKeepingDelegates(owner, to, profileId); + vm.stopPrank(); + } + + // Scenarios + + function testTransferFromKeepingDelegates_withApprove(address to) public { + vm.assume(to != address(0)); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: approvedProfileCreator, + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + vm.prank(approvedProfileCreator); + uint256 profileId = permissionlessCreator.createProfileUsingCredits(createProfileParams, delegates); + + assertEq(hub.ownerOf(profileId), approvedProfileCreator); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(permissionlessCreator.getProfileCreatorUsingCredits(profileId), approvedProfileCreator); + + vm.startPrank(approvedProfileCreator); + hub.approve(address(permissionlessCreator), profileId); + permissionlessCreator.transferFromKeepingDelegates(approvedProfileCreator, to, profileId); + vm.stopPrank(); + + assertEq(hub.ownerOf(profileId), to); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + } + + function testTransferFromKeepingDelegates_withApprovalForAll(address to) public { + vm.assume(to != address(0)); + + Types.CreateProfileParams memory createProfileParams = Types.CreateProfileParams({ + to: approvedProfileCreator, + followModule: address(0), + followModuleInitData: '' + }); + + address[] memory delegates = new address[](1); + delegates[0] = makeAddr('DE0'); + + vm.prank(approvedProfileCreator); + uint256 profileId = permissionlessCreator.createProfileUsingCredits(createProfileParams, delegates); + + assertEq(hub.ownerOf(profileId), approvedProfileCreator); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + assertEq(permissionlessCreator.getProfileCreatorUsingCredits(profileId), approvedProfileCreator); + + vm.startPrank(approvedProfileCreator); + hub.setApprovalForAll(address(permissionlessCreator), true); + permissionlessCreator.transferFromKeepingDelegates(approvedProfileCreator, to, profileId); + vm.stopPrank(); + + assertEq(hub.ownerOf(profileId), to); + assertTrue(hub.isDelegatedExecutorApproved(profileId, delegates[0])); + } // Credit Provider functions - // Negatives + // Negatives - testCannot_IncreaseCredit_IfNotCreditProvider - testCannot_DecreaseCredit_IfNotCreditProvider - testCannot_IncreaseCredit_IfTrustRevoked + function testCannot_IncreaseCredit_IfNotCreditProvider(address notCreditProvider) public { + vm.assume(permissionlessCreator.isCreditProvider(notCreditProvider) == false); - // Scenarios + vm.expectRevert(PermissionlessCreator.OnlyCreditProviders.selector); + vm.prank(notCreditProvider); + permissionlessCreator.increaseCredits(approvedProfileCreator, 1); + } - testIncreaseCredit - testDecreaseCredit + function testCannot_DecreaseCredit_IfNotCreditProvider(address notCreditProvider) public { + vm.assume(permissionlessCreator.isCreditProvider(notCreditProvider) == false); + + vm.expectRevert(PermissionlessCreator.OnlyCreditProviders.selector); + vm.prank(notCreditProvider); + permissionlessCreator.decreaseCredits(approvedProfileCreator, 1); + } + + function testCannot_IncreaseCredit_IfTrustRevoked() public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setTrustRevoked(approvedProfileCreator, true); + + vm.expectRevert(PermissionlessCreator.NotAllowed.selector); + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(approvedProfileCreator, 1); + } + + // Scenarios + + function testIncreaseCredit(address profileCreator) public { + vm.assume(profileCreator != address(0)); + + uint256 creditsBefore = permissionlessCreator.getCreditBalance(profileCreator); + + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(profileCreator, 123); + + uint256 creditsAfter = permissionlessCreator.getCreditBalance(profileCreator); + + assertEq(creditsBefore + 123, creditsAfter); + } + + function testDecreaseCredit(address profileCreator) public { + vm.assume(profileCreator != address(0)); + + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(profileCreator, 123456); + + uint256 creditsBefore = permissionlessCreator.getCreditBalance(profileCreator); + + vm.prank(creditProvider); + permissionlessCreator.decreaseCredits(profileCreator, 123); + + uint256 creditsAfter = permissionlessCreator.getCreditBalance(profileCreator); + + assertEq(creditsBefore - 123, creditsAfter); + } // Owner functions - // Negatives + // Negatives - testCannot_WithdrawCredits_IfNotOwner - testCannot_AddCreditProvider_IfNotOwner - testCannot_RemoveCreditProvider_IfNotOwner - testCannot_SetProfileCreationPrice_IfNotOwner - testCannot_SetHandleCreationPrice_IfNotOwner - testCannot_SetHandleLengthMin_IfNotOwner - testCannot_SetTrustRevoked_IfNotOwner + function testCannot_WithdrawFunds_IfNotOwner(address notOwner) public { + vm.assume(notOwner != permissionlessCreatorOwner); + vm.assume(notOwner != address(0)); - // Scenarios + vm.expectRevert('Ownable: caller is not the owner'); + vm.prank(notOwner); + permissionlessCreator.withdrawFunds(); + } - testWithdrawCredits - testAddCreditProvider - testRemoveCreditProvider - testSetProfileCreationPrice - testSetHandleCreationPrice - testSetHandleLengthMin - testSetTrustRevoked + function testCannot_AddCreditProvider_IfNotOwner(address notOwner, address newCreditProvider) public { + vm.assume(notOwner != permissionlessCreatorOwner); + vm.assume(notOwner != address(0)); + vm.assume(newCreditProvider != address(0)); + + vm.expectRevert('Ownable: caller is not the owner'); + vm.prank(notOwner); + permissionlessCreator.addCreditProvider(newCreditProvider); + } + + function testCannot_RemoveCreditProvider_IfNotOwner(address notOwner, address existingCreditProvider) public { + vm.assume(notOwner != permissionlessCreatorOwner); + vm.assume(notOwner != address(0)); + vm.assume(existingCreditProvider != address(0)); + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.addCreditProvider(existingCreditProvider); + + vm.expectRevert('Ownable: caller is not the owner'); + vm.prank(notOwner); + permissionlessCreator.removeCreditProvider(existingCreditProvider); + + assertTrue(permissionlessCreator.isCreditProvider(existingCreditProvider)); + } + + function testCannot_SetProfileCreationPrice_IfNotOwner(address notOwner, uint128 newPrice) public { + vm.assume(notOwner != permissionlessCreatorOwner); + vm.assume(notOwner != address(0)); + + vm.expectRevert('Ownable: caller is not the owner'); + vm.prank(notOwner); + permissionlessCreator.setProfileCreationPrice(newPrice); + } + + function testCannot_SetHandleCreationPrice_IfNotOwner(address notOwner, uint128 newPrice) public { + vm.assume(notOwner != permissionlessCreatorOwner); + vm.assume(notOwner != address(0)); + + vm.expectRevert('Ownable: caller is not the owner'); + vm.prank(notOwner); + permissionlessCreator.setHandleCreationPrice(newPrice); + } + + function testCannot_SetHandleLengthMin_IfNotOwner(address notOwner, uint8 newMinLength) public { + vm.assume(notOwner != permissionlessCreatorOwner); + vm.assume(notOwner != address(0)); + + vm.expectRevert('Ownable: caller is not the owner'); + vm.prank(notOwner); + permissionlessCreator.setHandleLengthMin(newMinLength); + } + + function testCannot_SetTrustRevoked_IfNotOwner(address notOwner, address targetAddress, bool newStatus) public { + vm.assume(notOwner != permissionlessCreatorOwner); + vm.assume(notOwner != address(0)); + vm.assume(targetAddress != address(0)); + + vm.expectRevert('Ownable: caller is not the owner'); + vm.prank(notOwner); + permissionlessCreator.setTrustRevoked(targetAddress, newStatus); + } + + // Scenarios + + function testWithdrawCredits() public { + vm.deal(address(permissionlessCreator), 123456789); + + uint256 contractBalanceBefore = address(permissionlessCreator).balance; + uint256 ownerBalanceBefore = permissionlessCreatorOwner.balance; + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.withdrawFunds(); + + uint256 contractBalanceAfter = address(permissionlessCreator).balance; + uint256 ownerBalanceAfter = permissionlessCreatorOwner.balance; + + assertEq(contractBalanceAfter, 0); + assertEq(ownerBalanceBefore + contractBalanceBefore, ownerBalanceAfter); + } + + function testAddCreditProvider(address newCreditProvider) public { + vm.assume(newCreditProvider != address(0)); + vm.assume(permissionlessCreator.isCreditProvider(newCreditProvider) == false); + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.addCreditProvider(newCreditProvider); + + assertTrue(permissionlessCreator.isCreditProvider(newCreditProvider)); + } + + function testRemoveCreditProvider(address existingCreditProvider) public { + vm.assume(existingCreditProvider != address(0)); + + if (permissionlessCreator.isCreditProvider(existingCreditProvider) == false) { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.addCreditProvider(existingCreditProvider); + } + + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.removeCreditProvider(existingCreditProvider); + + assertTrue(permissionlessCreator.isCreditProvider(existingCreditProvider) == false); + } + + function testSetProfileCreationPrice(uint128 newPrice) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setProfileCreationPrice(newPrice); + + assertEq(permissionlessCreator.getProfileCreationPrice(), newPrice); + } + + function testSetHandleCreationPrice(uint128 newPrice) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setHandleCreationPrice(newPrice); + + assertEq(permissionlessCreator.getHandleCreationPrice(), newPrice); + } + + function testSetHandleLengthMin(uint8 newMinLength) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setHandleLengthMin(newMinLength); + + assertEq(permissionlessCreator.getHandleLengthMin(), newMinLength); + } + + function testSetTrustRevoked(address targetAddress, bool newStatus) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setTrustRevoked(targetAddress, newStatus); + + assertEq(permissionlessCreator.isTrustRevoked(targetAddress), newStatus); + } // Getters - testGetProfileWithHandleCreationPrice - testGetProfileCreationPrice - testGetHandleCreationPrice - testGetHandleLengthMin - testIsTrustRevoked - testIsCreditProvider - testGetCreditBalance + function testGetProfileWithHandleCreationPrice() public { + assertEq( + permissionlessCreator.getProfileWithHandleCreationPrice(), + permissionlessCreator.getProfileCreationPrice() + permissionlessCreator.getHandleCreationPrice() + ); -*/ + vm.startPrank(permissionlessCreatorOwner); + permissionlessCreator.setProfileCreationPrice(123); + permissionlessCreator.setHandleCreationPrice(456); + vm.stopPrank(); + + assertEq( + permissionlessCreator.getProfileWithHandleCreationPrice(), + permissionlessCreator.getProfileCreationPrice() + permissionlessCreator.getHandleCreationPrice() + ); + } + + function testGetProfileCreationPrice(uint128 newPrice) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setProfileCreationPrice(newPrice); + + assertEq(permissionlessCreator.getProfileCreationPrice(), newPrice); + } + + function testGetHandleCreationPrice(uint128 newPrice) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setHandleCreationPrice(newPrice); + + assertEq(permissionlessCreator.getHandleCreationPrice(), newPrice); + } + + function testGetHandleLengthMin(uint8 newMinLength) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setHandleLengthMin(newMinLength); + + assertEq(permissionlessCreator.getHandleLengthMin(), newMinLength); + } + + function testIsTrustRevoked(address targetAddress, bool newStatus) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.setTrustRevoked(targetAddress, newStatus); + + assertEq(permissionlessCreator.isTrustRevoked(targetAddress), newStatus); + } + + function testIsCreditProvider(address targetAddress) public { + vm.prank(permissionlessCreatorOwner); + permissionlessCreator.addCreditProvider(targetAddress); + + assertTrue(permissionlessCreator.isCreditProvider(targetAddress)); + } + + function testGetCreditBalance(address targetAddress, uint256 addBalance, uint256 subBalance) public { + addBalance = bound(addBalance, 0, 100 ether); + subBalance = bound(subBalance, 0, addBalance); + uint256 balanceBefore = permissionlessCreator.getCreditBalance(targetAddress); + + vm.prank(creditProvider); + permissionlessCreator.increaseCredits(targetAddress, addBalance); + + assertEq(permissionlessCreator.getCreditBalance(targetAddress), balanceBefore + addBalance); + + vm.prank(creditProvider); + permissionlessCreator.decreaseCredits(targetAddress, subBalance); + + assertEq(permissionlessCreator.getCreditBalance(targetAddress), balanceBefore + addBalance - subBalance); + } +}