mirror of
https://github.com/lens-protocol/core.git
synced 2026-01-10 22:58:08 -05:00
refactor: Removed the need to pass follow NFT Ids to toggleFollow() calls, tests adapted.
This commit is contained in:
@@ -566,13 +566,11 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
/// @inheritdoc ILensHub
|
||||
function toggleFollow(
|
||||
uint256[] calldata profileIds,
|
||||
uint256[] calldata followNFTIds,
|
||||
bool[] calldata enables
|
||||
) external override whenNotPaused {
|
||||
InteractionLogic.toggleFollow(
|
||||
msg.sender,
|
||||
profileIds,
|
||||
followNFTIds,
|
||||
enables,
|
||||
_profileById,
|
||||
_profileIdByHandleHash
|
||||
@@ -591,7 +589,6 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
abi.encode(
|
||||
TOGGLE_FOLLOW_WITH_SIG_TYPEHASH,
|
||||
keccak256(abi.encodePacked(vars.profileIds)),
|
||||
keccak256(abi.encodePacked(vars.followNFTIds)),
|
||||
keccak256(abi.encodePacked(vars.enables)),
|
||||
sigNonces[vars.follower]++,
|
||||
vars.sig.deadline
|
||||
@@ -604,7 +601,6 @@ contract LensHub is ILensHub, LensNFTBase, VersionedInitializable, LensMultiStat
|
||||
InteractionLogic.toggleFollow(
|
||||
vars.follower,
|
||||
vars.profileIds,
|
||||
vars.followNFTIds,
|
||||
vars.enables,
|
||||
_profileById,
|
||||
_profileIdByHandleHash
|
||||
|
||||
@@ -247,14 +247,9 @@ interface ILensHub {
|
||||
* NOTE: `profileIds`, `followNFTIds` and `enables` arrays must be of the same length.
|
||||
*
|
||||
* @param profileIds The token ID array of the profiles.
|
||||
* @param followNFTIds The token ID array of the followNFTs.
|
||||
* @param enables The array of booleans to enable/disable follows.
|
||||
*/
|
||||
function toggleFollow(
|
||||
uint256[] calldata profileIds,
|
||||
uint256[] calldata followNFTIds,
|
||||
bool[] calldata enables
|
||||
) external;
|
||||
function toggleFollow(uint256[] calldata profileIds, bool[] calldata enables) external;
|
||||
|
||||
/**
|
||||
* @notice Toggle Follows a given profiles via signature with the specified parameters.
|
||||
|
||||
@@ -347,14 +347,12 @@ library DataTypes {
|
||||
*
|
||||
* @param follower The follower which is the message signer.
|
||||
* @param profileIds The token ID array of the profiles.
|
||||
* @param followNFTIds The token ID array of the followNFTs.
|
||||
* @param enables The array of booleans to enable/disable follows.
|
||||
* @param sig The EIP712Signature struct containing the follower's signature.
|
||||
*/
|
||||
struct ToggleFollowWithSigData {
|
||||
address follower;
|
||||
uint256[] profileIds;
|
||||
uint256[] followNFTIds;
|
||||
bool[] enables;
|
||||
EIP712Signature sig;
|
||||
}
|
||||
|
||||
@@ -161,20 +161,17 @@ library InteractionLogic {
|
||||
*
|
||||
* @param follower The address executing the follow.
|
||||
* @param profileIds The token ID array of the profiles.
|
||||
* @param followNFTIds The token ID array of the followNFTs.
|
||||
* @param enables The array of booleans to enable/disable follows.
|
||||
* @param _profileById A pointer to the storage mapping of profile structs by profile ID.
|
||||
*/
|
||||
function toggleFollow(
|
||||
address follower,
|
||||
uint256[] calldata profileIds,
|
||||
uint256[] calldata followNFTIds,
|
||||
bool[] calldata enables,
|
||||
mapping(uint256 => DataTypes.ProfileStruct) storage _profileById,
|
||||
mapping(bytes32 => uint256) storage _profileIdByHandleHash
|
||||
) external {
|
||||
if (profileIds.length != followNFTIds.length || profileIds.length != enables.length)
|
||||
revert Errors.ArrayMismatch();
|
||||
if (profileIds.length != enables.length) revert Errors.ArrayMismatch();
|
||||
for (uint256 i = 0; i < profileIds.length; ++i) {
|
||||
address followNFT = _profileById[profileIds[i]].followNFT;
|
||||
if (followNFT == address(0)) revert Errors.FollowInvalid();
|
||||
@@ -183,8 +180,7 @@ library InteractionLogic {
|
||||
if (_profileIdByHandleHash[keccak256(bytes(handle))] == 0)
|
||||
revert Errors.TokenDoesNotExist();
|
||||
|
||||
if (ERC721Time(followNFT).ownerOf(followNFTIds[i]) != follower)
|
||||
revert Errors.FollowInvalid();
|
||||
if (ERC721Time(followNFT).balanceOf(follower) == 0) revert Errors.FollowInvalid();
|
||||
}
|
||||
emit Events.FollowsToggled(follower, profileIds, enables, block.timestamp);
|
||||
}
|
||||
|
||||
@@ -420,14 +420,12 @@ export async function getFollowWithSigParts(
|
||||
|
||||
export async function getToggleFollowWithSigParts(
|
||||
profileIds: string[] | number[],
|
||||
followNFTIds: string[] | number[],
|
||||
enables: boolean[],
|
||||
nonce: number,
|
||||
deadline: string
|
||||
): Promise<{ v: number; r: string; s: string }> {
|
||||
const msgParams = buildToggleFollowWithSigParams(
|
||||
profileIds,
|
||||
followNFTIds,
|
||||
enables,
|
||||
nonce,
|
||||
deadline
|
||||
@@ -800,7 +798,6 @@ const buildFollowWithSigParams = (
|
||||
|
||||
const buildToggleFollowWithSigParams = (
|
||||
profileIds: string[] | number[],
|
||||
followNFTIds: string[] | number[],
|
||||
enables: boolean[],
|
||||
nonce: number,
|
||||
deadline: string
|
||||
@@ -808,7 +805,6 @@ const buildToggleFollowWithSigParams = (
|
||||
types: {
|
||||
ToggleFollowWithSig: [
|
||||
{ name: 'profileIds', type: 'uint256[]' },
|
||||
{ name: 'followNFTIds', type: 'uint256[]' },
|
||||
{ name: 'enables', type: 'bool[]' },
|
||||
{ name: 'nonce', type: 'uint256' },
|
||||
{ name: 'deadline', type: 'uint256' },
|
||||
@@ -817,7 +813,6 @@ const buildToggleFollowWithSigParams = (
|
||||
domain: domain(),
|
||||
value: {
|
||||
profileIds: profileIds,
|
||||
followNFTIds: followNFTIds,
|
||||
enables: enables,
|
||||
nonce: nonce,
|
||||
deadline: deadline,
|
||||
|
||||
@@ -25,13 +25,14 @@ import {
|
||||
MOCK_PROFILE_URI,
|
||||
userAddress,
|
||||
MOCK_FOLLOW_NFT_URI,
|
||||
OTHER_MOCK_URI,
|
||||
} from '../../__setup.spec';
|
||||
|
||||
const getTokenId = async (address) => {
|
||||
const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
|
||||
return await followNFT.tokenOfOwnerByIndex(address, 0);
|
||||
};
|
||||
// const getTokenId = async (address: string) => {
|
||||
// const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
// const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
|
||||
// return await followNFT.tokenOfOwnerByIndex(address, 0);
|
||||
// };
|
||||
|
||||
makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
beforeEach(async function () {
|
||||
@@ -52,63 +53,93 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
context('Generic', function () {
|
||||
context('Negatives', function () {
|
||||
it('UserTwo should fail to toggle follow with an incorrect profileId', async function () {
|
||||
const id = await getTokenId(userTwoAddress);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID + 1], [id], [true])
|
||||
lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID + 1], [true])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to toggle follow with array mismatch', async function () {
|
||||
const id = await getTokenId(userTwoAddress);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID, FIRST_PROFILE_ID], [id], [])
|
||||
lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID, FIRST_PROFILE_ID], [])
|
||||
).to.be.revertedWith(ERRORS.ARRAY_MISMATCH);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to toggle follow from a profile that has been burned', async function () {
|
||||
await expect(lensHub.burn(FIRST_PROFILE_ID)).to.not.be.reverted;
|
||||
const id = await getTokenId(userTwoAddress);
|
||||
await expect(
|
||||
lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID], [id], [true])
|
||||
lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID], [true])
|
||||
).to.be.revertedWith(ERRORS.TOKEN_DOES_NOT_EXIST);
|
||||
});
|
||||
|
||||
it('UserTwo should fail to toggle follow for a followNFT that is not the owner.', async function () {
|
||||
const id = await getTokenId(userThreeAddress);
|
||||
it('UserTwo should fail to toggle follow for a followNFT that is not owned by them', async function () {
|
||||
const followNFTAddress = await lensHub.getFollowNFT(FIRST_PROFILE_ID);
|
||||
const followNFT = FollowNFT__factory.connect(followNFTAddress, user);
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID], [id], [true])
|
||||
followNFT.connect(userTwo).transferFrom(userTwoAddress, userAddress, 1)
|
||||
).to.not.be.reverted;
|
||||
|
||||
await expect(
|
||||
lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID], [true])
|
||||
).to.be.revertedWith(ERRORS.FOLLOW_INVALID);
|
||||
});
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('UserTwo should toggle follow with true value, correct events should be emitted', async function () {
|
||||
const id = await getTokenId(userTwoAddress);
|
||||
|
||||
const tx = lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID], [id], [true]);
|
||||
it('UserTwo should toggle follow with true value, correct event should be emitted', async function () {
|
||||
const tx = lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID], [true]);
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'ToggleFollowNFT', [
|
||||
FIRST_PROFILE_ID,
|
||||
matchEvent(receipt, 'FollowsToggled', [
|
||||
userTwoAddress,
|
||||
true,
|
||||
[FIRST_PROFILE_ID],
|
||||
[true],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
it('UserTwo should toggle follow with false value, correct events should be emitted', async function () {
|
||||
const id = await getTokenId(userTwoAddress);
|
||||
|
||||
const tx = lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID], [id], [false]);
|
||||
it('User should create another profile, userTwo follows, then toggles both, one true, one false, correct event should be emitted', async function () {
|
||||
await expect(
|
||||
lensHub.createProfile({
|
||||
to: userAddress,
|
||||
handle: 'otherhandle',
|
||||
imageURI: OTHER_MOCK_URI,
|
||||
followModule: ZERO_ADDRESS,
|
||||
followModuleData: [],
|
||||
followNFTURI: MOCK_FOLLOW_NFT_URI,
|
||||
})
|
||||
).to.not.be.reverted;
|
||||
await expect(lensHub.connect(userTwo).follow([FIRST_PROFILE_ID + 1], [[]])).to.not.be.reverted;
|
||||
|
||||
const tx = lensHub
|
||||
.connect(userTwo)
|
||||
.toggleFollow([FIRST_PROFILE_ID, FIRST_PROFILE_ID + 1], [true, false]);
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'ToggleFollowNFT', [
|
||||
FIRST_PROFILE_ID,
|
||||
matchEvent(receipt, 'FollowsToggled', [
|
||||
userTwoAddress,
|
||||
false,
|
||||
[FIRST_PROFILE_ID, FIRST_PROFILE_ID + 1],
|
||||
[true, false],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('UserTwo should toggle follow with false value, correct event should be emitted', async function () {
|
||||
// const id = await getTokenId(userTwoAddress);
|
||||
|
||||
const tx = lensHub.connect(userTwo).toggleFollow([FIRST_PROFILE_ID], [false]);
|
||||
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'FollowsToggled', [
|
||||
userTwoAddress,
|
||||
[FIRST_PROFILE_ID],
|
||||
[false],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
@@ -120,11 +151,8 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
it('TestWallet should fail to toggle follow with sig with signature deadline mismatch', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const id = await getTokenId(testWallet.address);
|
||||
|
||||
const { v, r, s } = await getToggleFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[id.toNumber()],
|
||||
[true],
|
||||
nonce,
|
||||
'0'
|
||||
@@ -133,7 +161,6 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
lensHub.toggleFollowWithSig({
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
followNFTIds: [id],
|
||||
enables: [true],
|
||||
sig: {
|
||||
v,
|
||||
@@ -148,11 +175,8 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
it('TestWallet should fail to toggle follow with sig with invalid deadline', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const id = await getTokenId(testWallet.address);
|
||||
|
||||
const { v, r, s } = await getToggleFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[id.toNumber()],
|
||||
[true],
|
||||
nonce,
|
||||
'0'
|
||||
@@ -161,7 +185,6 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
lensHub.toggleFollowWithSig({
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
followNFTIds: [id],
|
||||
enables: [true],
|
||||
sig: {
|
||||
v,
|
||||
@@ -176,11 +199,8 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
it('TestWallet should fail to toggle follow with sig with invalid nonce', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const id = await getTokenId(testWallet.address);
|
||||
|
||||
const { v, r, s } = await getToggleFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[id.toNumber()],
|
||||
[true],
|
||||
nonce + 1,
|
||||
MAX_UINT256
|
||||
@@ -190,7 +210,6 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
lensHub.toggleFollowWithSig({
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
followNFTIds: [id],
|
||||
enables: [true],
|
||||
sig: {
|
||||
v,
|
||||
@@ -204,12 +223,9 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
|
||||
it('TestWallet should fail to toggle follow a nonexistent profile with sig', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const id = await getTokenId(testWallet.address);
|
||||
const INVALID_PROFILE = FIRST_PROFILE_ID + 1;
|
||||
const { v, r, s } = await getToggleFollowWithSigParts(
|
||||
[INVALID_PROFILE],
|
||||
[id.toNumber()],
|
||||
[true],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
@@ -218,7 +234,6 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
lensHub.toggleFollowWithSig({
|
||||
follower: testWallet.address,
|
||||
profileIds: [INVALID_PROFILE],
|
||||
followNFTIds: [id.toNumber()],
|
||||
enables: [true],
|
||||
sig: {
|
||||
v,
|
||||
@@ -232,14 +247,11 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
});
|
||||
|
||||
context('Scenarios', function () {
|
||||
it('TestWallet should toggle follow profile 1 to true with sig, correct events should be emitted ', async function () {
|
||||
it('TestWallet should toggle follow profile 1 to true with sig, correct event should be emitted ', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const id = await getTokenId(testWallet.address);
|
||||
|
||||
const { v, r, s } = await getToggleFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[id.toNumber()],
|
||||
[true],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
@@ -248,7 +260,6 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
const tx = lensHub.toggleFollowWithSig({
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
followNFTIds: [id.toNumber()],
|
||||
enables: [true],
|
||||
sig: {
|
||||
v,
|
||||
@@ -261,22 +272,20 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'ToggleFollowNFT', [
|
||||
FIRST_PROFILE_ID,
|
||||
matchEvent(receipt, 'FollowsToggled', [
|
||||
testWallet.address,
|
||||
true,
|
||||
[FIRST_PROFILE_ID],
|
||||
[true],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
it('TestWallet should toggle follow profile 1 to false with sig, correct events should be emitted ', async function () {
|
||||
it('TestWallet should toggle follow profile 1 to false with sig, correct event should be emitted ', async function () {
|
||||
const nonce = (await lensHub.sigNonces(testWallet.address)).toNumber();
|
||||
|
||||
const id = await getTokenId(testWallet.address);
|
||||
const enabled = false;
|
||||
const { v, r, s } = await getToggleFollowWithSigParts(
|
||||
[FIRST_PROFILE_ID],
|
||||
[id.toNumber()],
|
||||
[enabled],
|
||||
nonce,
|
||||
MAX_UINT256
|
||||
@@ -285,7 +294,6 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
const tx = lensHub.toggleFollowWithSig({
|
||||
follower: testWallet.address,
|
||||
profileIds: [FIRST_PROFILE_ID],
|
||||
followNFTIds: [id.toNumber()],
|
||||
enables: [enabled],
|
||||
sig: {
|
||||
v,
|
||||
@@ -298,10 +306,10 @@ makeSuiteCleanRoom('ToggleFollowing', function () {
|
||||
const receipt = await waitForTx(tx);
|
||||
|
||||
expect(receipt.logs.length).to.eq(1);
|
||||
matchEvent(receipt, 'ToggleFollowNFT', [
|
||||
FIRST_PROFILE_ID,
|
||||
matchEvent(receipt, 'FollowsToggled', [
|
||||
testWallet.address,
|
||||
enabled,
|
||||
[FIRST_PROFILE_ID],
|
||||
[enabled],
|
||||
await getTimestamp(),
|
||||
]);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user