// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import 'test/base/TestSetup.t.sol'; import 'contracts/libraries/constants/Types.sol'; import {Typehash} from 'contracts/libraries/constants/Typehash.sol'; import {Typehash as NamespacesTypehash} from 'contracts/namespaces/constants/Typehash.sol'; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; import {Address} from '@openzeppelin/contracts/utils/Address.sol'; contract BaseTest is TestSetup { using Strings for string; using Address for address; function testBaseTest() public { // Prevents being counted in Foundry Coverage } // Empty setUp for easier overriding in other tests, otherwise you need to override from TestSetup and is confusing. function setUp() public virtual override { super.setUp(); } function _effectivelyDisableProfileGuardian(address wallet) internal { _effectivelyDisableGuardian(address(hub), wallet); } function _effectivelyDisableGuardian(address nft, address wallet) internal { if (_isProfileGuardianEnabled(wallet)) { vm.prank(wallet); // TODO: Fix this if we move disableTokenGuardian to its own interface LensHub(nft).DANGER__disableTokenGuardian(); vm.warp(LensHub(nft).getTokenGuardianDisablingTimestamp(wallet)); } } function _isProfileGuardianEnabled(address wallet) internal view returns (bool) { return !wallet.isContract() && (hub.getTokenGuardianDisablingTimestamp(wallet) == 0 || block.timestamp < hub.getTokenGuardianDisablingTimestamp(wallet)); } function _boundPk(uint256 fuzzedUint256) internal view returns (uint256 fuzzedPk) { return bound(fuzzedUint256, 1, ISSECP256K1_CURVE_ORDER - 1); } function _isLensHubProxyAdmin(address proxyAdminCandidate) internal view returns (bool) { address proxyAdmin = address(uint160(uint256(vm.load(address(hub), ADMIN_SLOT)))); return proxyAdminCandidate == proxyAdmin; } function _getSetProfileMetadataURITypedDataHash( uint256 profileId, string memory metadataURI, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( Typehash.SET_PROFILE_METADATA_URI, profileId, _encodeUsingEip712Rules(metadataURI), nonce, deadline ) ); return _calculateDigest(structHash); } function _getSetFollowModuleTypedDataHash( uint256 profileId, address followModule, bytes memory followModuleInitData, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( Typehash.SET_FOLLOW_MODULE, profileId, followModule, _encodeUsingEip712Rules(followModuleInitData), nonce, deadline ) ); return _calculateDigest(structHash); } function _getChangeDelegatedExecutorsConfigTypedDataHash( uint256 delegatorProfileId, uint64 configNumber, address[] memory delegatedExecutors, bool[] memory approvals, bool switchToGivenConfig, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( Typehash.CHANGE_DELEGATED_EXECUTORS_CONFIG, delegatorProfileId, _encodeUsingEip712Rules(delegatedExecutors), _encodeUsingEip712Rules(approvals), configNumber, switchToGivenConfig, nonce, deadline ) ); return _calculateDigest(structHash); } function _getPostTypedDataHash( uint256 profileId, string memory contentURI, address[] memory actionModules, bytes[] memory actionModulesInitDatas, address referenceModule, bytes memory referenceModuleInitData, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( Typehash.POST, profileId, _encodeUsingEip712Rules(contentURI), _encodeUsingEip712Rules(actionModules), _encodeUsingEip712Rules(actionModulesInitDatas), referenceModule, _encodeUsingEip712Rules(referenceModuleInitData), nonce, deadline ) ); return _calculateDigest(structHash); } function _getPostTypedDataHash( Types.PostParams memory postParams, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { return _getPostTypedDataHash({ profileId: postParams.profileId, contentURI: postParams.contentURI, actionModules: postParams.actionModules, actionModulesInitDatas: postParams.actionModulesInitDatas, referenceModule: postParams.referenceModule, referenceModuleInitData: postParams.referenceModuleInitData, nonce: nonce, deadline: deadline }); } // We need this to deal with stack too deep: struct ReferenceParamsForAbiEncode { bytes32 typehash; uint256 profileId; bytes32 contentURIHash; uint256 pointedProfileId; uint256 pointedPubId; bytes32 referrerProfileIds; bytes32 referrerPubIds; bytes32 referenceModuleDataHash; bytes32 actionModules; bytes32 actionModulesInitDataHash; address referenceModule; bytes32 referenceModuleInitDataHash; uint256 nonce; uint256 deadline; } function _abiEncode( ReferenceParamsForAbiEncode memory referenceParamsForAbiEncode ) private pure returns (bytes memory) { return abi.encode(referenceParamsForAbiEncode); } function _getCommentTypedDataHash( Types.CommentParams memory commentParams, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( _abiEncode( ReferenceParamsForAbiEncode( Typehash.COMMENT, commentParams.profileId, _encodeUsingEip712Rules(commentParams.contentURI), commentParams.pointedProfileId, commentParams.pointedPubId, _encodeUsingEip712Rules(commentParams.referrerProfileIds), _encodeUsingEip712Rules(commentParams.referrerPubIds), _encodeUsingEip712Rules(commentParams.referenceModuleData), _encodeUsingEip712Rules(commentParams.actionModules), _encodeUsingEip712Rules(commentParams.actionModulesInitDatas), commentParams.referenceModule, _encodeUsingEip712Rules(commentParams.referenceModuleInitData), nonce, deadline ) ) ); return _calculateDigest(structHash); } function _getQuoteTypedDataHash( Types.QuoteParams memory quoteParams, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( _abiEncode( ReferenceParamsForAbiEncode( Typehash.QUOTE, quoteParams.profileId, _encodeUsingEip712Rules(quoteParams.contentURI), quoteParams.pointedProfileId, quoteParams.pointedPubId, _encodeUsingEip712Rules(quoteParams.referrerProfileIds), _encodeUsingEip712Rules(quoteParams.referrerPubIds), _encodeUsingEip712Rules(quoteParams.referenceModuleData), _encodeUsingEip712Rules(quoteParams.actionModules), _encodeUsingEip712Rules(quoteParams.actionModulesInitDatas), quoteParams.referenceModule, _encodeUsingEip712Rules(quoteParams.referenceModuleInitData), nonce, deadline ) ) ); return _calculateDigest(structHash); } function _getMirrorTypedDataHash( Types.MirrorParams memory mirrorParams, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( Typehash.MIRROR, mirrorParams.profileId, _encodeUsingEip712Rules(mirrorParams.metadataURI), mirrorParams.pointedProfileId, mirrorParams.pointedPubId, _encodeUsingEip712Rules(mirrorParams.referrerProfileIds), _encodeUsingEip712Rules(mirrorParams.referrerPubIds), _encodeUsingEip712Rules(mirrorParams.referenceModuleData), nonce, deadline ) ); return _calculateDigest(structHash); } function _getFollowTypedDataHash( uint256 followerProfileId, uint256[] memory idsOfProfilesToFollow, uint256[] memory followTokenIds, bytes[] memory datas, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( Typehash.FOLLOW, followerProfileId, _encodeUsingEip712Rules(idsOfProfilesToFollow), _encodeUsingEip712Rules(followTokenIds), _encodeUsingEip712Rules(datas), nonce, deadline ) ); return _calculateDigest(structHash); } function _getActTypedDataHash( Types.PublicationActionParams memory publicationActionParams, uint256 nonce, uint256 deadline ) internal view returns (bytes32) { bytes32 structHash = keccak256( abi.encode( Typehash.ACT, publicationActionParams.publicationActedProfileId, publicationActionParams.publicationActedId, publicationActionParams.actorProfileId, _encodeUsingEip712Rules(publicationActionParams.referrerProfileIds), _encodeUsingEip712Rules(publicationActionParams.referrerPubIds), publicationActionParams.actionModuleAddress, _encodeUsingEip712Rules(publicationActionParams.actionModuleData), nonce, deadline ) ); return _calculateDigest(structHash); } function _calculateDigest(bytes32 structHash) internal view returns (bytes32) { return keccak256(abi.encodePacked('\x19\x01', domainSeparator, structHash)); } function _getSigStruct( uint256 pKey, bytes32 digest, uint256 deadline ) internal pure returns (Types.EIP712Signature memory) { return _getSigStruct(vm.addr(pKey), pKey, digest, deadline); } function _getSigStruct( address signer, uint256 pKey, bytes32 digest, uint256 deadline ) internal pure returns (Types.EIP712Signature memory) { (uint8 v, bytes32 r, bytes32 s) = vm.sign(pKey, digest); return Types.EIP712Signature(signer, v, r, s, deadline); } function _toLegacyV1Pub(uint256 profileId, uint256 pubId, address referenceModule, address collectModule) internal { uint256 PUBLICATIONS_MAPPING_SLOT = 20; uint256 publicationSlot; { // NOTE: Quotes are converted into V1 comments. Types.PublicationType pubType = hub.getPublicationType(profileId, pubId); if (pubType == Types.PublicationType.Nonexistent) { revert('Cannot convert unexistent or already V1 publications.'); } else if (pubType == Types.PublicationType.Mirror && collectModule != address(0)) { revert('Legacy V1 mirrors cannot have collect module.'); } else if (pubType != Types.PublicationType.Mirror && collectModule == address(0)) { revert('Legacy V1 non-mirror publications requires a non-zero collect module.'); } assembly { mstore(0, profileId) mstore(32, PUBLICATIONS_MAPPING_SLOT) mstore(32, keccak256(0, 64)) mstore(0, pubId) publicationSlot := keccak256(0, 64) } uint256 REFERENCE_MODULE_OFFSET = 3; uint256 referenceModuleSlot = publicationSlot + REFERENCE_MODULE_OFFSET; vm.store({ target: address(hub), slot: bytes32(referenceModuleSlot), value: bytes32(uint256(uint160(referenceModule))) }); uint256 COLLECT_MODULE_OFFSET = 4; uint256 collectModuleSlot = publicationSlot + COLLECT_MODULE_OFFSET; vm.store({ target: address(hub), slot: bytes32(collectModuleSlot), value: bytes32(uint256(uint160(collectModule))) }); uint256 firstSlotOffsetToWipe = 5; uint256 lastSlotOffsetToWipe = 8; for (uint256 offset = firstSlotOffsetToWipe; offset <= lastSlotOffsetToWipe; offset++) { vm.store({target: address(hub), slot: bytes32(publicationSlot + offset), value: 0}); } } { uint256 ACTION_MODULES_OFFSET = 8; uint256 ACTION_MODULES_MAPPING_SLOT = publicationSlot + ACTION_MODULES_OFFSET; _setActionModuleInPublicationStorage( ACTION_MODULES_MAPPING_SLOT, _getDefaultPostParams().actionModules[0], false ); _setActionModuleInPublicationStorage( ACTION_MODULES_MAPPING_SLOT, _getDefaultCommentParams().actionModules[0], false ); _setActionModuleInPublicationStorage( ACTION_MODULES_MAPPING_SLOT, _getDefaultQuoteParams().actionModules[0], false ); } } function _setCollectModuleAsIfItWasWhitelistedInLensV1(address collectModule) internal { _setCollectModuleWhitelistedInLensV1Storage(collectModule, true); } function _setCollectModuleWhitelistedInLensV1Storage(address collectModule, bool whitelisted) internal { uint256 COLLECT_MODULE_WHITELISTED_MAPPING_SLOT = 15; uint256 collectModuleWhitelistSlot; assembly { mstore(0, collectModule) mstore(32, COLLECT_MODULE_WHITELISTED_MAPPING_SLOT) collectModuleWhitelistSlot := keccak256(0, 64) } bytes32 whitelistedAsBytes32 = bytes32(uint256(whitelisted ? 1 : 0)); vm.store({target: address(hub), slot: bytes32(collectModuleWhitelistSlot), value: whitelistedAsBytes32}); } function _setActionModuleInPublicationStorage( uint256 actionModuleMappingSlot, address module, bool isEnabled ) internal { bytes32 slot; assembly { mstore(0, module) mstore(32, actionModuleMappingSlot) slot := keccak256(0, 64) } vm.store({target: address(hub), slot: slot, value: bytes32(uint256(isEnabled ? 1 : 0))}); } function _isV1LegacyPub(Types.PublicationMemory memory pub) internal pure returns (bool) { return uint8(pub.pubType) == 0; } function _encodeUsingEip712Rules(bytes[] memory bytesArray) internal pure returns (bytes32) { bytes32[] memory bytesArrayEncodedElements = new bytes32[](bytesArray.length); uint256 i; while (i < bytesArray.length) { // A `bytes` type is encoded as its keccak256 hash. bytesArrayEncodedElements[i] = keccak256(bytesArray[i]); unchecked { ++i; } } // An array is encoded as the keccak256 hash of the concatenation of their encoded elements. return _encodeUsingEip712Rules(bytesArrayEncodedElements); } function _encodeUsingEip712Rules(bool[] memory boolArray) internal pure returns (bytes32) { return keccak256(abi.encodePacked(boolArray)); } function _encodeUsingEip712Rules(address[] memory addressArray) internal pure returns (bytes32) { return keccak256(abi.encodePacked(addressArray)); } function _encodeUsingEip712Rules(uint256[] memory uint256Array) internal pure returns (bytes32) { return keccak256(abi.encodePacked(uint256Array)); } function _encodeUsingEip712Rules(bytes32[] memory bytes32Array) internal pure returns (bytes32) { return keccak256(abi.encodePacked(bytes32Array)); } function _encodeUsingEip712Rules(string memory stringValue) internal pure returns (bytes32) { return keccak256(bytes(stringValue)); } function _encodeUsingEip712Rules(bytes memory bytesValue) internal pure returns (bytes32) { return keccak256(bytesValue); } function _transferHandle(address to, uint256 handleId) internal { address initialHandleHolder = lensHandles.ownerOf(handleId); vm.prank(initialHandleHolder); lensHandles.transferFrom(initialHandleHolder, to, handleId); } function _transferProfile(address to, uint256 profileId) internal { address initialProfileHolder = hub.ownerOf(profileId); _effectivelyDisableProfileGuardian(initialProfileHolder); vm.prank(initialProfileHolder); hub.transferFrom(initialProfileHolder, to, profileId); } }