function batchMigrateFollows(
    uint256[] calldata followerProfileIds,
    uint256[] calldata idsOfProfileFollowed,
    uint256[] calldata followTokenIds
) external {
    MigrationLib.batchMigrateFollows(followerProfileIds, idsOfProfileFollowed, followTokenIds);
}
function _migrateFollow(
    uint256 followerProfileId,
    uint256 idOfProfileFollowed,
    uint256 followTokenId
) private {
    uint48 mintTimestamp = FollowNFT(StorageLib.getProfile(idOfProfileFollowed).followNFT).tryMigrate({
        followerProfileId: followerProfileId,
        followerProfileOwner: StorageLib.getTokenData(followerProfileId).owner,
        idOfProfileFollowed: idOfProfileFollowed,
        followTokenId: followTokenId
    });
    // `mintTimestamp` will be 0 if:
    // - Follow NFT was already migrated
    // - Follow NFT does not exist or was burnt
    // - Follower profile Owner is different from Follow NFT Owner
    if (mintTimestamp != 0) {
        emit Events.Followed({
            followerProfileId: followerProfileId,
            idOfProfileFollowed: idOfProfileFollowed,
            followTokenIdAssigned: followTokenId,
            followModuleData: '',
            processFollowModuleReturnData: '',
            timestamp: mintTimestamp // The only case where this won't match block.timestamp is during the migration
        });
    }
}
function tryMigrate(
    uint256 followerProfileId,
    address followerProfileOwner,
    uint256 idOfProfileFollowed,
    uint256 followTokenId
) external onlyHub returns (uint48) {
    // Migrated FollowNFTs should have `originalFollowTimestamp` set
    if (_followDataByFollowTokenId[followTokenId].originalFollowTimestamp != 0) {
        return 0; // Already migrated
    }

    if (_followedProfileId != idOfProfileFollowed) {
        revert Errors.InvalidParameter();
    }

    if (!_exists(followTokenId)) {
        return 0; // Doesn't exist
    }

    address followTokenOwner = ownerOf(followTokenId);

    // ProfileNFT and FollowNFT should be in the same account
    if (followerProfileOwner != followTokenOwner) {
        return 0; // Not holding both Profile & Follow NFTs together
    }

    unchecked {
        ++_followerCount;
    }

    _followTokenIdByFollowerProfileId[followerProfileId] = followTokenId;

    uint48 mintTimestamp = uint48(StorageLib.getTokenData(followTokenId).mintTimestamp);

    _followDataByFollowTokenId[followTokenId].followerProfileId = uint160(followerProfileId);
    _followDataByFollowTokenId[followTokenId].originalFollowTimestamp = mintTimestamp;
    _followDataByFollowTokenId[followTokenId].followTimestamp = mintTimestamp;

    super._burn(followTokenId);
    return mintTimestamp;
}
interface LensHubV1 {
    struct ProfileV1 {
        uint256 pubCount; // offset 0
        address followModule; // offset 1
        address followNFT; // offset 2
        string __DEPRECATED__handle; // offset 3
        string imageURI; // offset 4
        string __DEPRECATED__followNFTURI;
    }

    function getProfile(uint256 profileId) external view returns (ProfileV1 memory);
}
address followNFTAddress = LensHubV1(address(hub)).getProfile(idOfProfileFollowed).followNFT;
function testMigrateBlockedFollowerPoC() public onlyFork {
    uint256 idOfProfileFollowed = 8;

    uint256[] memory idsOfProfileFollowed = new uint256[](10);
    uint256[] memory followTokenIds = new uint256[](10);
    bool[] memory blockStatuses = new bool[](10);

    for (uint256 i = 0; i < 10; i++) {
        uint256 followTokenId = i + 1;

        idsOfProfileFollowed[i] = idOfProfileFollowed;
        followTokenIds[i] = followTokenId;

        blockStatuses[i] = true;
    }

    // block followers
    address targetProfileOwner = hub.ownerOf(idOfProfileFollowed);
    vm.prank(targetProfileOwner);

    hub.setBlockStatus(
        idOfProfileFollowed,
        followerProfileIds,
        blockStatuses
    );

    // check block status
    assertEq(hub.isBlocked(followerProfileIds[0], idOfProfileFollowed), true);

    // migrate
    hub.batchMigrateFollows(followerProfileIds, idsOfProfileFollowed, followTokenIds);

    // check follow work 
    address followNFTAddress = LensHubV1(address(hub)).getProfile(idOfProfileFollowed).followNFT;
    assertEq(FollowNFT(followNFTAddress).isFollowing(followerProfileIds[0]), true); // blocked, but followed!
}
