From b5d00a99bed9b8f5d1e24fd87f7ffe67d9ee3634 Mon Sep 17 00:00:00 2001 From: Peter Michael Date: Mon, 20 Jun 2022 18:46:35 -0400 Subject: [PATCH] (WIP) refactor: Refactored to reduce storage pointers passed. --- contracts/core/LensHub.sol | 5 +- contracts/libraries/Constants.sol | 1 + contracts/libraries/GeneralLib.sol | 45 ++------- contracts/libraries/Helpers.sol | 10 +- contracts/libraries/InteractionHelpers.sol | 106 ++++++++++++++++++--- contracts/libraries/MetaTxHelpers.sol | 13 +-- 6 files changed, 112 insertions(+), 68 deletions(-) diff --git a/contracts/core/LensHub.sol b/contracts/core/LensHub.sol index 08827e0..55d462b 100644 --- a/contracts/core/LensHub.sol +++ b/contracts/core/LensHub.sol @@ -346,8 +346,7 @@ contract LensHub is whenNotPaused returns (uint256[] memory) { - return - GeneralLib.follow(msg.sender, profileIds, datas, _profileById, _profileIdByHandleHash); + return GeneralLib.follow(msg.sender, profileIds, datas); } /// @inheritdoc ILensHub @@ -357,7 +356,7 @@ contract LensHub is whenNotPaused returns (uint256[] memory) { - return GeneralLib.followWithSig(vars, _profileById, _profileIdByHandleHash); + return GeneralLib.followWithSig(vars); } /// @inheritdoc ILensHub diff --git a/contracts/libraries/Constants.sol b/contracts/libraries/Constants.sol index 440553b..280ea71 100644 --- a/contracts/libraries/Constants.sol +++ b/contracts/libraries/Constants.sol @@ -36,6 +36,7 @@ uint256 constant NAME_SLOT_GT_31 = 0x290decd9548b62a8d60345a988386fc84ba6bc95484 // Profile struct offsets uint256 constant PROFILE_FOLLOW_MODULE_OFFSET = 1; +uint256 constant PROFILE_FOLLOW_NFT_OFFSET = 2; uint256 constant PROFILE_HANDLE_OFFSET = 3; uint256 constant PROFILE_IMAGE_URI_OFFSET = 4; uint256 constant PROFILE_FOLLOW_NFT_URI_OFFSET = 5; diff --git a/contracts/libraries/GeneralLib.sol b/contracts/libraries/GeneralLib.sol index 2c966db..c5350c9 100644 --- a/contracts/libraries/GeneralLib.sol +++ b/contracts/libraries/GeneralLib.sol @@ -173,7 +173,6 @@ library GeneralLib { let slot := add(keccak256(0, 64), PROFILE_FOLLOW_MODULE_OFFSET) sstore(slot, followModule) } - followModuleReturnData = _initFollowModule( profileId, vars.followModule, @@ -333,26 +332,15 @@ library GeneralLib { * @param follower The address executing the follow. * @param profileIds The array of profile token IDs to follow. * @param followModuleDatas The array of follow module data parameters to pass to each profile's follow module. - * @param _profileById A pointer to the storage mapping of profile structs by profile ID. - * @param _profileIdByHandleHash A pointer to the storage mapping of profile IDs by handle hash. * * @return uint256[] An array of integers representing the minted follow NFTs token IDs. */ function follow( address follower, uint256[] calldata profileIds, - bytes[] calldata followModuleDatas, - mapping(uint256 => DataTypes.ProfileStruct) storage _profileById, - mapping(bytes32 => uint256) storage _profileIdByHandleHash + bytes[] calldata followModuleDatas ) external returns (uint256[] memory) { - return - InteractionHelpers.follow( - follower, - profileIds, - followModuleDatas, - _profileById, - _profileIdByHandleHash - ); + return InteractionHelpers.follow(follower, profileIds, followModuleDatas); } /** @@ -361,20 +349,12 @@ library GeneralLib { * * @param vars the FollowWithSigData struct containing the relevant parameters. */ - function followWithSig( - DataTypes.FollowWithSigData calldata vars, - mapping(uint256 => DataTypes.ProfileStruct) storage _profileById, - mapping(bytes32 => uint256) storage _profileIdByHandleHash - ) external returns (uint256[] memory) { + function followWithSig(DataTypes.FollowWithSigData calldata vars) + external + returns (uint256[] memory) + { MetaTxHelpers.baseFollowWithSig(vars); - return - InteractionHelpers.follow( - vars.follower, - vars.profileIds, - vars.datas, - _profileById, - _profileIdByHandleHash - ); + return InteractionHelpers.follow(vars.follower, vars.profileIds, vars.datas); } /** @@ -545,17 +525,12 @@ library GeneralLib { address followModule, bytes calldata followModuleInitData ) private { - address currentFollowModule; - uint256 slot; assembly { mstore(0, profileId) mstore(32, PROFILE_BY_ID_MAPPING_SLOT) - slot := add(keccak256(0, 64), PROFILE_FOLLOW_MODULE_OFFSET) - currentFollowModule := sload(slot) - } - - if (followModule != currentFollowModule) { - assembly { + let slot := add(keccak256(0, 64), PROFILE_FOLLOW_MODULE_OFFSET) + let currentFollowModule := sload(slot) + if iszero(eq(followModule, currentFollowModule)) { sstore(slot, followModule) } } diff --git a/contracts/libraries/Helpers.sol b/contracts/libraries/Helpers.sol index 0e4f37e..f739498 100644 --- a/contracts/libraries/Helpers.sol +++ b/contracts/libraries/Helpers.sol @@ -11,8 +11,7 @@ import './Constants.sol'; * @title Helpers * @author Lens Protocol * - * @notice This is a library that only contains a single function that is used in the hub contract as well as in - * both the publishing logic and interaction logic libraries. + * @notice This is a library that contains helper internal functions used by both the Hub and the GeneralLib. */ library Helpers { /** @@ -22,16 +21,13 @@ library Helpers { * @param profileId The token ID of the profile that published the given publication. * @param pubId The publication ID of the given publication. * - * @return tuple First, the pointed publication's publishing profile ID, and second, the pointed publication's ID. + * @return tuple First, the pointed publication's publishing profile ID, and second, the pointed publication's ID. * If the passed publication is not a mirror, this returns the given publication. */ function getPointedIfMirror(uint256 profileId, uint256 pubId) internal view - returns ( - uint256, - uint256 - ) + returns (uint256, uint256) { uint256 slot; address collectModule; diff --git a/contracts/libraries/InteractionHelpers.sol b/contracts/libraries/InteractionHelpers.sol index cc01f54..d4cfeea 100644 --- a/contracts/libraries/InteractionHelpers.sol +++ b/contracts/libraries/InteractionHelpers.sol @@ -30,24 +30,37 @@ library InteractionHelpers { function follow( address follower, uint256[] calldata profileIds, - bytes[] calldata followModuleDatas, - mapping(uint256 => DataTypes.ProfileStruct) storage _profileById, - mapping(bytes32 => uint256) storage _profileIdByHandleHash - ) internal returns (uint256[] memory) { + bytes[] calldata followModuleDatas //, + ) + internal + returns ( + uint256[] memory + ) + { if (profileIds.length != followModuleDatas.length) revert Errors.ArrayMismatch(); uint256[] memory tokenIds = new uint256[](profileIds.length); for (uint256 i = 0; i < profileIds.length; ) { - string memory handle = _profileById[profileIds[i]].handle; - if (_profileIdByHandleHash[keccak256(bytes(handle))] != profileIds[i]) - revert Errors.TokenDoesNotExist(); - - address followModule = _profileById[profileIds[i]].followModule; - address followNFT = _profileById[profileIds[i]].followNFT; - + uint256 profileId = profileIds[i]; + _validateProfileExistsViaHandle(profileId); + uint256 followNFTSlot; + address followModule; + address followNFT; + assembly { + mstore(0, profileId) + mstore(32, PROFILE_BY_ID_MAPPING_SLOT) + // The follow NFT offset is 2, the follow module offset is 1, + // so we just need to subtract 1 instead of recalculating the slot. + followNFTSlot := add(keccak256(0, 64), PROFILE_FOLLOW_NFT_OFFSET) + followModule := sload(sub(followNFTSlot,1)) + followNFT := sload(followNFTSlot) + } + if (followNFT == address(0)) { - followNFT = _deployFollowNFT(profileIds[i]); - _profileById[profileIds[i]].followNFT = followNFT; + followNFT = _deployFollowNFT(profileId); + assembly { + sstore(followNFTSlot, followNFT) + } } tokenIds[i] = IFollowNFT(followNFT).mint(follower); @@ -55,7 +68,7 @@ library InteractionHelpers { if (followModule != address(0)) { IFollowModule(followModule).processFollow( follower, - profileIds[i], + profileId, followModuleDatas[i] ); } @@ -79,7 +92,7 @@ library InteractionHelpers { ) internal returns (uint256) { (uint256 rootProfileId, uint256 rootPubId, address rootCollectModule) = Helpers .getPointedIfMirrorWithCollectModule(profileId, pubId); - + uint256 tokenId; // Avoids stack too deep { @@ -196,4 +209,67 @@ library InteractionHelpers { block.timestamp ); } + + function _validateProfileExistsViaHandle(uint256 profileId) private view { + bool shouldRevert; + assembly { + // Load the free memory pointer, where we'll return the value + let ptr := mload(64) + + // Load the slot, which either contains the name + 2*length if length < 32 or + // 2*length+1 if length >= 32, and the actual string starts at slot keccak256(slot) + mstore(0, profileId) + mstore(32, PROFILE_BY_ID_MAPPING_SLOT) + let slot := add(keccak256(0, 64), PROFILE_HANDLE_OFFSET) + + let slotLoad := sload(slot) + let size + // Determine if the length > 32 by checking the lowest order bit, meaning the string + // itself is stored at keccak256(slot) + switch and(slotLoad, 1) + case 0 { + // The name is in the same slot + // Determine the size by dividing the last byte's value by 2 + size := shr(1, and(slotLoad, 255)) + + // Store the size in the first slot + mstore(ptr, size) + + // Store the actual string in the second slot (without the size) + mstore(add(ptr, 32), and(slotLoad, not(255))) + } + case 1 { + // The handle is not in the same slot + // Determine the size by dividing the value in the whole slot minus 1 by 2 + size := shr(1, sub(slotLoad, 1)) + + // Store the size in the first slot + mstore(ptr, size) + + // Compute the total memory slots we need, this is (size + 31) / 32 + let totalMemorySlots := shr(5, add(size, 31)) + + mstore(0, slot) + let handleSlot := keccak256(0, 32) + + // Iterate through the words in memory and store the string word by word + // prettier-ignore + for { let i := 0 } lt(i, totalMemorySlots) { i := add(i, 1) } { + mstore(add(add(ptr, 32), mul(32, i)), sload(add(handleSlot, i))) + } + } + + let handleHash := keccak256(add(ptr, 32), size) + mstore(0, handleHash) + mstore(32, PROFILE_ID_BY_HANDLE_HASH_MAPPING_SLOT) + let handleHashSlot := keccak256(0, 64) + let resolvedProfileId := sload(handleHashSlot) + if iszero(eq(resolvedProfileId, profileId)) { + shouldRevert := true + } + // Store the new memory pointer in the free memory pointer slot + mstore(64, add(add(ptr, 32), size)) + } + if (shouldRevert) revert Errors.TokenDoesNotExist(); + } } diff --git a/contracts/libraries/MetaTxHelpers.sol b/contracts/libraries/MetaTxHelpers.sol index 2e1f5ad..1bbd8dd 100644 --- a/contracts/libraries/MetaTxHelpers.sol +++ b/contracts/libraries/MetaTxHelpers.sol @@ -381,27 +381,25 @@ library MetaTxHelpers { // 2*length+1 if length >= 32, and the actual string starts at slot keccak256(NAME_SLOT) let slotLoad := sload(NAME_SLOT) + let size // Determine if the length > 32 by checking the lowest order bit, meaning the string // itself is stored at keccak256(NAME_SLOT) switch and(slotLoad, 1) case 0 { // The name is in the same slot // Determine the size by dividing the last byte's value by 2 - let size := shr(1, and(slotLoad, 255)) + size := shr(1, and(slotLoad, 255)) // Store the size in the first slot mstore(ptr, size) // Store the actual string in the second slot (without the size) mstore(add(ptr, 32), and(slotLoad, not(255))) - - // Store the new memory pointer in the free memory pointer slot - mstore(64, add(add(ptr, 32), size)) } case 1 { // The name is not in the same slot // Determine the size by dividing the value in the whole slot minus 1 by 2 - let size := shr(1, sub(slotLoad, 1)) + size := shr(1, sub(slotLoad, 1)) // Store the size in the first slot mstore(ptr, size) @@ -414,10 +412,9 @@ library MetaTxHelpers { for { let i := 0 } lt(i, totalMemorySlots) { i := add(i, 1) } { mstore(add(add(ptr, 32), mul(32, i)), sload(add(NAME_SLOT_GT_31, i))) } - - // Store the new memory pointer in the free memory pointer slot - mstore(64, add(add(ptr, 32), size)) } + // Store the new memory pointer in the free memory pointer slot + mstore(64, add(add(ptr, 32), size)) } // Return a memory pointer to the name (which always starts with the size at the first slot) return ptr;