From d720c1384728afb975bad138dceebf8b26c98914 Mon Sep 17 00:00:00 2001 From: donosonaumczuk Date: Thu, 5 Jan 2023 20:43:11 -0300 Subject: [PATCH] test: Follow tests added --- TestsList.md | 64 +- .../mocks/MockFollowModuleWithRevertFlag.sol | 32 + test/foundry/FollowTest.t.sol | 564 ++++++++++++++---- test/foundry/helpers/SignatureHelpers.sol | 10 - 4 files changed, 510 insertions(+), 160 deletions(-) create mode 100644 contracts/mocks/MockFollowModuleWithRevertFlag.sol diff --git a/TestsList.md b/TestsList.md index 95bbc24..ec28843 100644 --- a/TestsList.md +++ b/TestsList.md @@ -28,25 +28,25 @@ Scenarios Following Generic Negatives -[ ] UserTwo should fail to follow a nonexistent profile -[ ] UserTwo should fail to follow with array mismatch -[ ] UserTwo should fail to follow a profile that has been burned -[ ] UserTwo should fail to follow profile with id 0 +[X] UserTwo should fail to follow a nonexistent profile +[X] UserTwo should fail to follow with array mismatch +[X] UserTwo should fail to follow a profile that has been burned +[X] UserTwo should fail to follow profile with id 0 Scenarios -[ ] UserTwo should follow profile 1, receive a followNFT with ID 1, followNFT properties should be correct -[ ] UserTwo should follow profile 1 twice, receiving followNFTs with IDs 1 and 2 -[ ] UserTwo should follow profile 1 3 times in the same call, receive IDs 1,2 and 3 -[ ] Should return the expected token IDs when following profiles +[X] UserTwo should follow profile 1, receive a followNFT with ID 1, followNFT properties should be correct +[-] UserTwo should follow profile 1 twice, receiving followNFTs with IDs 1 and 2 +[-] UserTwo should follow profile 1 3 times in the same call, receive IDs 1,2 and 3 +[X] Should return the expected token IDs when following profiles Meta-tx Negatives -[ ] TestWallet should fail to follow with sig with signature deadline mismatch -[ ] TestWallet should fail to follow with sig with invalid deadline -[ ] TestWallet should fail to follow with sig with invalid nonce -[ ] TestWallet should fail to follow a nonexistent profile with sig -[ ] TestWallet should sign attempt to follow with sig, cancel with empty permitForAll, then fail to follow with sig +[X] TestWallet should fail to follow with sig with signature deadline mismatch +[X] TestWallet should fail to follow with sig with invalid deadline +[X] TestWallet should fail to follow with sig with invalid nonce +[X] TestWallet should fail to follow a nonexistent profile with sig +[-] TestWallet should sign attempt to follow with sig, cancel with empty permitForAll, then fail to follow with sig Scenarios -[ ] TestWallet should follow profile 1 with sig, receive a follow NFT with ID 1, follow NFT name and symbol should be correct -[ ] TestWallet should follow profile 1 with sig twice in the same call, receive follow NFTs with IDs 1 and 2 +[X] TestWallet should follow profile 1 with sig, receive a follow NFT with ID 1, follow NFT name and symbol should be correct +[X] TestWallet should follow profile 1 with sig twice in the same call, receive follow NFTs with IDs 1 and 2 Governance Functions Negatives @@ -201,7 +201,9 @@ Negatives [X] TestWallet should fail to set default profile with sig with signature deadline mismatch [X] TestWallet should fail to set default profile with sig with invalid deadline [X] TestWallet should fail to set default profile with sig with invalid nonce + + [-] TestWallet should sign attempt to set default profile with sig, cancel with empty permitForAll, then fail to set default profile with sig Scenarios [X] TestWallet should set the default profile with sig @@ -312,26 +314,26 @@ Follow NFT generic Negatives [ ] User should follow, and fail to re-initialize the follow NFT -[ ] User should follow, userTwo should fail to burn user's follow NFT -[ ] User should follow, then fail to mint a follow NFT directly -[ ] User should follow, then fail to get the power at a future block -[ ] user should follow, then fail to get the URI for a token that does not exist +[-] User should follow, userTwo should fail to burn user's follow NFT +[-] User should follow, then fail to mint a follow NFT directly +[-] User should follow, then fail to get the power at a future block +[-] user should follow, then fail to get the URI for a token that does not exist Scenarios -[ ] User should follow, then burn their follow NFT, governance power is zero before and after -[ ] User should follow, delegate to themself, governance power should be zero before the last block, and 1 at the current block -[ ] User and userTwo should follow, governance power should be zero, then users delegate multiple times, governance power should be accurate throughout -[ ] User and userTwo should follow, delegate to themselves, 10 blocks later user delegates to userTwo, 10 blocks later both delegate to user, governance power should be accurate throughout -[ ] user and userTwo should follow, user delegates to userTwo twice, governance power should be accurate -[ ] User and userTwo should follow, then transfer their NFTs to the helper contract, then the helper contract batch delegates to user one, then user two, governance power should be accurate -[ ] user should follow, then get the URI for their token, URI should be accurate +[-] User should follow, then burn their follow NFT, governance power is zero before and after +[-] User should follow, delegate to themself, governance power should be zero before the last block, and 1 at the current block +[-] User and userTwo should follow, governance power should be zero, then users delegate multiple times, governance power should be accurate throughout +[-] User and userTwo should follow, delegate to themselves, 10 blocks later user delegates to userTwo, 10 blocks later both delegate to user, governance power should be accurate throughout +[-] user and userTwo should follow, user delegates to userTwo twice, governance power should be accurate +[-] User and userTwo should follow, then transfer their NFTs to the helper contract, then the helper contract batch delegates to user one, then user two, governance power should be accurate +[-] user should follow, then get the URI for their token, URI should be accurate meta-tx negatives -[ ] TestWallet should fail to delegate with sig with signature deadline mismatch -[ ] TestWallet should fail to delegate with sig with invalid deadline -[ ] TestWallet should fail to delegate with sig with invalid nonce -[ ] TestWallet should sign attempt to delegate by sig, cancel with empty permitForAll, then fail to delegate by sig +[-] TestWallet should fail to delegate with sig with signature deadline mismatch +[-] TestWallet should fail to delegate with sig with invalid deadline +[-] TestWallet should fail to delegate with sig with invalid nonce +[-] TestWallet should sign attempt to delegate by sig, cancel with empty permitForAll, then fail to delegate by sig Scenarios -[ ] TestWallet should delegate by sig to user, governance power should be accurate before and after +[-] TestWallet should delegate by sig to user, governance power should be accurate before and after Lens NFT Base Functionality generic diff --git a/contracts/mocks/MockFollowModuleWithRevertFlag.sol b/contracts/mocks/MockFollowModuleWithRevertFlag.sol new file mode 100644 index 0000000..e14c970 --- /dev/null +++ b/contracts/mocks/MockFollowModuleWithRevertFlag.sol @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.15; + +import {IFollowModule} from '../interfaces/IFollowModule.sol'; + +/** + * @dev This is a simple mock follow module to be used for testing revert cases on processFollow. + */ +contract MockFollowModuleWithRevertFlag is IFollowModule { + error MockFollowModuleReverted(); + + function initializeFollowModule( + uint256 profileId, + address executor, + bytes calldata data + ) external pure override returns (bytes memory) { + return new bytes(0); + } + + function processFollow( + uint256 followerProfileId, + uint256 followTokenId, + address executor, + uint256 profileId, + bytes calldata data + ) external pure override { + if (abi.decode(data, (bool))) { + revert MockFollowModuleReverted(); + } + } +} diff --git a/test/foundry/FollowTest.t.sol b/test/foundry/FollowTest.t.sol index 2d7c276..e9dff1f 100644 --- a/test/foundry/FollowTest.t.sol +++ b/test/foundry/FollowTest.t.sol @@ -2,151 +2,477 @@ pragma solidity ^0.8.13; import './base/BaseTest.t.sol'; +import './MetaTxNegatives.t.sol'; import {Strings} from '@openzeppelin/contracts/utils/Strings.sol'; -import './helpers/SignatureHelpers.sol'; +import './helpers/AssumptionHelpers.sol'; +import {IFollowNFT} from 'contracts/interfaces/IFollowNFT.sol'; +import '../../contracts/mocks/MockFollowModuleWithRevertFlag.sol'; -contract FollowTest is BaseTest, SignatureHelpers { +contract FollowTest is BaseTest, AssumptionHelpers { using Strings for uint256; + uint256 constant MINT_NEW_TOKEN = 0; + + address constant PROFILE_OWNER = address(0); + + uint256 constant targetProfileOwnerPk = 0xC0FFEE; + address targetProfileOwner; + uint256 targetProfileId; + + uint256 constant followerProfileOwnerPk = 0x7357; + address followerProfileOwner; uint256 followerProfileId; + uint256 constant alreadyFollowingProfileOwnerPk = 0xF01108; + address alreadyFollowingProfileOwner; + uint256 alreadyFollowingProfileId; + + address targetFollowNFTAddress; + + uint256 followTokenId; + + address followModuleWithRevertFlag; + function setUp() public virtual override { super.setUp(); - followerProfileId = _createProfile(me); + + targetProfileOwner = vm.addr(targetProfileOwnerPk); + targetProfileId = _createProfile(targetProfileOwner); + + followerProfileOwner = vm.addr(followerProfileOwnerPk); + followerProfileId = _createProfile(followerProfileOwner); + + alreadyFollowingProfileOwner = vm.addr(alreadyFollowingProfileOwnerPk); + alreadyFollowingProfileId = _createProfile(alreadyFollowingProfileOwner); + + followTokenId = _follow( + alreadyFollowingProfileOwner, + alreadyFollowingProfileId, + targetProfileId, + 0, + '' + )[0]; + + targetFollowNFTAddress = hub.getFollowNFT(targetProfileId); + followNFT = FollowNFT(targetFollowNFTAddress); + + followModuleWithRevertFlag = address(new MockFollowModuleWithRevertFlag()); + vm.prank(governance); + hub.whitelistFollowModule(followModuleWithRevertFlag, true); } // Negatives - // TODO + + function testCannotFollowIfPaused() public { + vm.prank(governance); + hub.setState(DataTypes.ProtocolState.Paused); + + vm.expectRevert(Errors.Paused.selector); + + _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray('') + }); + } + + function testCannotFollowIfBlocked() public { + vm.prank(targetProfileOwner); + hub.setBlockStatus(targetProfileId, _toUint256Array(followerProfileId), _toBoolArray(true)); + + vm.expectRevert(Errors.Blocked.selector); + + _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray('') + }); + } + + function testCannotFollowIfAmountOfTokenIdsPassedDiffersFromAmountOfProfilesToFollow() public { + vm.expectRevert(Errors.ArrayMismatch.selector); + + _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId, alreadyFollowingProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray('', '') + }); + } + + function testCannotFollowIfAmountOfDataForFollowModulePassedDiffersFromAmountOfProfilesToFollow() + public + { + vm.expectRevert(Errors.ArrayMismatch.selector); + + _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray('', '') + }); + } + + function testCannotFollowIfFollowerProfileDoesNotExist() public { + vm.prank(followerProfileOwner); + hub.burn(followerProfileId); + + vm.expectRevert(Errors.TokenDoesNotExist.selector); + + _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray('') + }); + } + + function testCannotFollowIfFollowedProfileHaveIdZero() public { + vm.expectRevert(Errors.TokenDoesNotExist.selector); + + _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(0), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray('') + }); + } + + function testCannotFollowIfProfileBeingFollowedDoesNotExist() public { + vm.prank(targetProfileOwner); + hub.burn(targetProfileId); + + vm.expectRevert(Errors.TokenDoesNotExist.selector); + + _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray('') + }); + } + + function testCannotFollowIfAlreadyFollowing() public { + vm.expectRevert(IFollowNFT.AlreadyFollowing.selector); + + _follow({ + pk: alreadyFollowingProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: alreadyFollowingProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray('') + }); + } + + function testCannotFollowIfFollowModuleRevertsWhileProcessingTheFollow() public { + vm.prank(targetProfileOwner); + hub.setFollowModule(targetProfileId, followModuleWithRevertFlag, ''); + + bool revertWhileProcessingFollow = true; + + vm.expectRevert(MockFollowModuleWithRevertFlag.MockFollowModuleReverted.selector); + + _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray(abi.encode(revertWhileProcessingFollow)) + }); + } // Positives - function testFollow() public { - assertEq(hub.getFollowNFT(newProfileId), address(0)); - uint256[] memory nftIds = _follow({ - msgSender: me, + function testFollowAsFollowerOwner() public { + vm.prank(targetProfileOwner); + hub.setFollowModule(targetProfileId, followModuleWithRevertFlag, ''); + + bytes memory followModuleData = abi.encode(false); + + uint256 expectedFollowTokenIdAssigned = followTokenId + 1; + + vm.expectEmit(true, false, false, true, address(hub)); + emit Events.Followed( + followerProfileId, + targetProfileId, + expectedFollowTokenIdAssigned, + followModuleData, + block.timestamp + ); + + vm.expectCall( + targetFollowNFTAddress, + abi.encodeCall( + followNFT.follow, + ( + followerProfileId, + followerProfileOwner, + followerProfileOwner, + false, + MINT_NEW_TOKEN + ) + ) + ); + + vm.expectCall( + followModuleWithRevertFlag, + abi.encodeCall( + IFollowModule.processFollow, + ( + followerProfileId, + MINT_NEW_TOKEN, + followerProfileOwner, + targetProfileId, + followModuleData + ) + ) + ); + + uint256[] memory assignedFollowTokenIds = _follow({ + pk: followerProfileOwnerPk, + isFollowerProfileOwner: true, followerProfileId: followerProfileId, - idOfProfileToFollow: newProfileId, - followTokenId: 0, - data: '' + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray(followModuleData) }); - FollowNFT nft = FollowNFT(hub.getFollowNFT(newProfileId)); - string memory expectedName = string( - abi.encodePacked(newProfileId.toString(), FOLLOW_NFT_NAME_SUFFIX) - ); - string memory expectedSymbol = string( - abi.encodePacked(newProfileId.toString(), FOLLOW_NFT_SYMBOL_SUFFIX) - ); - assertEq(nft.name(), expectedName); - assertEq(nft.symbol(), expectedSymbol); - assertEq(nftIds.length, 1); - assertEq(nftIds[0], 1); - assertEq(nft.getFollowerProfileId(1), followerProfileId); - assertEq(nft.getFollowTokenId(followerProfileId), 1); + assertEq(assignedFollowTokenIds.length, 1); + assertEq(assignedFollowTokenIds[0], expectedFollowTokenIdAssigned); + assertTrue(hub.isFollowing(followerProfileId, targetProfileId)); } - function testExecutorFollow() public { - hub.setDelegatedExecutorApproval(otherSigner, true); + function testFollowAsFollowerApprovedDelegatedExecutor(uint256 approvedDelegatedExecutorPk) + public + { + vm.assume(_isValidPk(approvedDelegatedExecutorPk)); + address approvedDelegatedExecutor = vm.addr(approvedDelegatedExecutorPk); + vm.assume(approvedDelegatedExecutor != address(0)); + vm.assume(approvedDelegatedExecutor != followerProfileOwner); - uint256[] memory nftIds = _follow({ - msgSender: otherSigner, + vm.prank(followerProfileOwner); + hub.setDelegatedExecutorApproval(approvedDelegatedExecutor, true); + + vm.prank(targetProfileOwner); + hub.setFollowModule(targetProfileId, followModuleWithRevertFlag, ''); + + bytes memory followModuleData = abi.encode(false); + + uint256 expectedFollowTokenIdAssigned = followTokenId + 1; + + vm.expectEmit(true, false, false, true, address(hub)); + emit Events.Followed( + followerProfileId, + targetProfileId, + expectedFollowTokenIdAssigned, + followModuleData, + block.timestamp + ); + + vm.expectCall( + targetFollowNFTAddress, + abi.encodeCall( + followNFT.follow, + ( + followerProfileId, + approvedDelegatedExecutor, + followerProfileOwner, + true, + MINT_NEW_TOKEN + ) + ) + ); + + vm.expectCall( + followModuleWithRevertFlag, + abi.encodeCall( + IFollowModule.processFollow, + ( + followerProfileId, + MINT_NEW_TOKEN, + approvedDelegatedExecutor, + targetProfileId, + followModuleData + ) + ) + ); + + uint256[] memory assignedFollowTokenIds = _follow({ + pk: approvedDelegatedExecutorPk, + isFollowerProfileOwner: false, followerProfileId: followerProfileId, - idOfProfileToFollow: newProfileId, - followTokenId: 0, - data: '' + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray(followModuleData) }); - FollowNFT nft = FollowNFT(hub.getFollowNFT(newProfileId)); - assertEq(nftIds.length, 1); - assertEq(nftIds[0], 1); - assertEq(nft.getFollowerProfileId(1), followerProfileId); - assertEq(nft.getFollowTokenId(followerProfileId), 1); + assertEq(assignedFollowTokenIds.length, 1); + assertEq(assignedFollowTokenIds[0], expectedFollowTokenIdAssigned); + assertTrue(hub.isFollowing(followerProfileId, targetProfileId)); } - // Meta-tx - // Negatives - // TODO - - // Positives - function testFollowWithSig() public { - assertEq(hub.getFollowNFT(newProfileId), address(0)); - - uint256[] memory profileIds = new uint256[](1); - profileIds[0] = newProfileId; - bytes[] memory datas = new bytes[](1); - datas[0] = ''; - uint256 nonce = 0; - uint256 deadline = type(uint256).max; - uint256 followerProfileId = _createProfile(otherSigner); - bytes32 digest = _getFollowTypedDataHash( - followerProfileId, - profileIds, - _toUint256Array(0), - datas, - nonce, - deadline - ); - - uint256[] memory nftIds = _followWithSig( - DataTypes.FollowWithSigData({ - delegatedSigner: address(0), - followerProfileId: followerProfileId, - idsOfProfilesToFollow: profileIds, - followTokenIds: _toUint256Array(0), - datas: datas, - sig: _getSigStruct(otherSignerKey, digest, deadline) - }) - ); - - FollowNFT nft = FollowNFT(hub.getFollowNFT(newProfileId)); - string memory expectedName = string( - abi.encodePacked(newProfileId.toString(), FOLLOW_NFT_NAME_SUFFIX) - ); - string memory expectedSymbol = string( - abi.encodePacked(newProfileId.toString(), FOLLOW_NFT_SYMBOL_SUFFIX) - ); - assertEq(nft.name(), expectedName); - assertEq(nft.symbol(), expectedSymbol); - assertEq(nftIds.length, 1); - assertEq(nftIds[0], 1); - assertEq(nft.getFollowerProfileId(1), followerProfileId); - assertEq(nft.getFollowTokenId(followerProfileId), 1); + function _follow( + uint256 pk, + bool isFollowerProfileOwner, + uint256 followerProfileId, + uint256[] memory idsOfProfilesToFollow, + uint256[] memory followTokenIds, + bytes[] memory datas + ) internal virtual returns (uint256[] memory) { + vm.prank(vm.addr(pk)); + return hub.follow(followerProfileId, idsOfProfilesToFollow, followTokenIds, datas); } - function testExecutorFollowWithSig() public { - vm.prank(otherSigner); - hub.setDelegatedExecutorApproval(profileOwner, true); - - uint256[] memory profileIds = new uint256[](1); - profileIds[0] = newProfileId; - bytes[] memory datas = new bytes[](1); - datas[0] = ''; - uint256 nonce = 0; - uint256 deadline = type(uint256).max; - uint256 followerProfileId = _createProfile(otherSigner); - bytes32 digest = _getFollowTypedDataHash( - followerProfileId, - profileIds, - _toUint256Array(0), - datas, - nonce, - deadline - ); - uint256[] memory nftIds = _followWithSig( - DataTypes.FollowWithSigData({ - delegatedSigner: profileOwner, - followerProfileId: followerProfileId, - idsOfProfilesToFollow: profileIds, - followTokenIds: _toUint256Array(0), - datas: datas, - sig: _getSigStruct(profileOwnerKey, digest, deadline) - }) - ); - - FollowNFT nft = FollowNFT(hub.getFollowNFT(newProfileId)); - assertEq(nftIds.length, 1); - assertEq(nftIds[0], 1); - assertEq(nft.getFollowerProfileId(1), followerProfileId); - assertEq(nft.getFollowTokenId(followerProfileId), 1); + function _refreshCachedNonces() internal virtual { + // Nothing to do there. + } +} + +contract FollowMetaTxTest is FollowTest, MetaTxNegatives { + mapping(address => uint256) cachedNonceByAddress; + + function setUp() public override(FollowTest, MetaTxNegatives) { + FollowTest.setUp(); + MetaTxNegatives.setUp(); + + cachedNonceByAddress[followerProfileOwner] = _getSigNonce(followerProfileOwner); + cachedNonceByAddress[alreadyFollowingProfileOwner] = _getSigNonce( + alreadyFollowingProfileOwner + ); + } + + function _follow( + uint256 pk, + bool isFollowerProfileOwner, + uint256 followerProfileId, + uint256[] memory idsOfProfilesToFollow, + uint256[] memory followTokenIds, + bytes[] memory datas + ) internal override returns (uint256[] memory) { + address signer = vm.addr(pk); + return + hub.followWithSig( + _getSignedData({ + signerPk: pk, + delegatedSigner: isFollowerProfileOwner ? PROFILE_OWNER : signer, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: idsOfProfilesToFollow, + followTokenIds: followTokenIds, + datas: datas, + nonce: cachedNonceByAddress[signer], + deadline: type(uint256).max + }) + ); + } + + function _executeMetaTx( + uint256 signerPk, + uint256 nonce, + uint256 deadline + ) internal virtual override { + hub.followWithSig( + _getSignedData({ + signerPk: signerPk, + delegatedSigner: PROFILE_OWNER, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: _toUint256Array(targetProfileId), + followTokenIds: _toUint256Array(MINT_NEW_TOKEN), + datas: _toBytesArray(''), + nonce: nonce, + deadline: deadline + }) + ); + } + + function _getDefaultMetaTxSignerPk() internal virtual override returns (uint256) { + return followerProfileOwnerPk; + } + + function _calculateFollowWithSigDigest( + uint256 followerProfileId, + uint256[] memory idsOfProfilesToFollow, + uint256[] memory followTokenIds, + bytes[] memory datas, + uint256 nonce, + uint256 deadline + ) internal returns (bytes32) { + bytes32[] memory dataHashes = new bytes32[](datas.length); + for (uint256 i = 0; i < datas.length; ) { + dataHashes[i] = keccak256(datas[i]); + unchecked { + ++i; + } + } + return + _calculateDigest( + keccak256( + abi.encode( + FOLLOW_WITH_SIG_TYPEHASH, + followerProfileId, + keccak256(abi.encodePacked(idsOfProfilesToFollow)), + keccak256(abi.encodePacked(followTokenIds)), + keccak256(abi.encodePacked(dataHashes)), + nonce, + deadline + ) + ) + ); + } + + function _getSignedData( + uint256 signerPk, + address delegatedSigner, + uint256 followerProfileId, + uint256[] memory idsOfProfilesToFollow, + uint256[] memory followTokenIds, + bytes[] memory datas, + uint256 nonce, + uint256 deadline + ) internal returns (DataTypes.FollowWithSigData memory) { + return + DataTypes.FollowWithSigData({ + delegatedSigner: delegatedSigner, + followerProfileId: followerProfileId, + idsOfProfilesToFollow: idsOfProfilesToFollow, + followTokenIds: followTokenIds, + datas: datas, + sig: _getSigStruct({ + pKey: signerPk, + digest: _calculateFollowWithSigDigest( + followerProfileId, + idsOfProfilesToFollow, + followTokenIds, + datas, + nonce, + deadline + ), + deadline: deadline + }) + }); + } + + function _refreshCachedNonces() internal override { + cachedNonceByAddress[followerProfileOwner] = _getSigNonce(followerProfileOwner); + cachedNonceByAddress[alreadyFollowingProfileOwner] = _getSigNonce( + alreadyFollowingProfileOwner + ); } } diff --git a/test/foundry/helpers/SignatureHelpers.sol b/test/foundry/helpers/SignatureHelpers.sol index 944381a..100ca54 100644 --- a/test/foundry/helpers/SignatureHelpers.sol +++ b/test/foundry/helpers/SignatureHelpers.sol @@ -163,16 +163,6 @@ contract SignatureHelpers { }); } - function _buildFollowWithSigData( - address delegatedSigner, - address follower, - uint256[] memory profileIds, - bytes[] memory datas, - DataTypes.EIP712Signature memory sig - ) internal pure returns (DataTypes.FollowWithSigData memory) { - return DataTypes.FollowWithSigData(delegatedSigner, follower, profileIds, datas, sig); - } - function _buildSetDefaultProfileWithSigData( address delegatedSigner, address wallet,