Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,5 @@ npm-debug.log*
.openzeppelin

/backup

/typechain
6 changes: 0 additions & 6 deletions babel.config.js

This file was deleted.

2 changes: 2 additions & 0 deletions babel.config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{
}
248 changes: 208 additions & 40 deletions contracts/PointSocial.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ pragma solidity >=0.8.0;
pragma experimental ABIEncoderV2;

import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
Expand All @@ -12,6 +14,9 @@ import "point-contract-manager/contracts/IIdentity.sol";

contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, PausableUpgradeable {
using Counters for Counters.Counter;

/************************* STORAGE LAYOUT START *************************/

Counters.Counter internal _postIds;
Counters.Counter internal _commentIds;
Counters.Counter internal _likeIds;
Expand Down Expand Up @@ -108,8 +113,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
Counters.Counter internal _dislikeIds;
mapping(uint256 => uint256[]) public dislikeIdsByPost;
mapping(address => uint256[]) public dislikeIdsByUser;
mapping(address => mapping(uint256 => uint256))
public dislikeIdByUserAndPost;
mapping(address => mapping(uint256 => uint256)) public dislikeIdByUserAndPost;
mapping(uint256 => Dislike) public dislikeById;

struct PostWithMetadata {
Expand All @@ -123,10 +127,45 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
uint256 dislikesCount;
bool liked;
bool disliked;
bool flagged;
}

/*
* Follow functions storage
*/
using EnumerableSet for EnumerableSet.AddressSet;

struct FollowConnections {
EnumerableSet.AddressSet following;
EnumerableSet.AddressSet followers;
EnumerableSet.AddressSet blocked;
}

mapping(address => FollowConnections) internal _followConnectionsByUser;

enum FollowAction {
Follow,
UnFollow,
Block,
UnBlock
}

event FollowEvent(
address indexed from,
address indexed to,
FollowAction action);

enum FeedType {
Discover,
Follow,
Fresh,
Top
}

/************************* STORAGE LAYOUT END *************************/

modifier postExists(uint256 _postId) {
require(postById[_postId].from != address(0), "Post does not exist");
require(postById[_postId].from != address(0), "ERROR_POST_DOES_NOT_EXISTS");
_;
}

Expand All @@ -135,7 +174,12 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
"ERROR_NOT_DEPLOYER"
);
_;
}
}

modifier onlyMigrator() {
require(msg.sender == _migrator, "ERROR_NOT_MIGRATOR");
_;
}

function initialize(
address identityContractAddr,
Expand Down Expand Up @@ -204,8 +248,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
uint256 postId,
bytes32 contents,
bytes32 image
) public whenNotPaused {
require(postById[postId].createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");
) public whenNotPaused postExists(postId) {
require(
msg.sender == postById[postId].from,
"ERROR_CANNOT_EDIT_OTHERS_POSTS"
Expand All @@ -223,8 +266,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
);
}

function deletePost(uint256 postId) public whenNotPaused {
require(postById[postId].createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");
function deletePost(uint256 postId) public whenNotPaused postExists(postId) {
require(
msg.sender == postById[postId].from,
"ERROR_CANNOT_DELETE_OTHERS_POSTS"
Expand All @@ -245,13 +287,8 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
);
}

function flagPost(uint256 postId) public whenNotPaused {
require(IIdentity(_identityContractAddr).isIdentityDeployer(_identityHandle, msg.sender),
"ERROR_PERMISSION_DENIED");
require(postById[postId].createdAt != 0, "ERROR_POST_DOES_NOT_EXISTS");

function flagPost(uint256 postId) public onlyDeployer whenNotPaused postExists(postId) {
postIsFlagged[postId] = !postIsFlagged[postId];

emit StateChange(postId, msg.sender, block.timestamp, Component.Post, Action.Flag);
}

Expand All @@ -264,42 +301,90 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
}

function getAllPostsLength() public view returns (uint256) {
uint256 length = 0;
for (uint256 i = 0; i < postIds.length; i++) {
if (postById[postIds[i]].createdAt > 0) {
length++;
}
return postIds.length;
}

function getLastPostId() public view returns (uint256) {
return (postIds.length > 0)? postIds[postIds.length-1] : 0;
}

function _includePost(uint256 postId, FeedType _type) internal view returns (bool) {
Post memory p = postById[postId];
if (_type == FeedType.Discover) {
return (!postIsFlagged[postId] && /* Filter flagged posts */
!(isBlocked(msg.sender, p.from) /* Filter blocked users */
|| isBlocked(p.from, msg.sender)) && /* Filter blocked users */
!(p.from == msg.sender) //&& /* Filter own posts */
//!isFollowing(msg.sender, p.from) /* Filter following users */
);
}
else if (_type == FeedType.Follow) {
return (isFollowing(msg.sender, p.from)); /* Filter following users */
}
else if (_type == FeedType.Top) {
return (!postIsFlagged[postId] && /* Filter flagged posts */
!(isBlocked(msg.sender, p.from)
|| isBlocked(p.from, msg.sender)) && /* Filter blocked users */
!(p.likesCount == 0)); /* Filter unvoted posts */
}
else {
return (!(isBlocked(msg.sender, p.from) /* Filter blocked users */
|| isBlocked(p.from, msg.sender)));
}
return length;
}

function getPaginatedPosts(uint256 cursor, uint256 howMany)
function getPaginatedPosts(uint256 _cursor, uint256 _howMany, FeedType _type)
public
view
returns (PostWithMetadata[] memory)
{
uint256 length = howMany;
if (length > postIds.length - cursor) {
length = postIds.length - cursor;
require(_howMany > 0, "ERROR_INVALID_AMOUNT");
require((_cursor > 0) && (_cursor <= postIds.length), "ERROR_INVALID_CURSOR");

uint256 length = _howMany;
if (length > _cursor) {
length = _cursor;
}

PostWithMetadata[] memory postsWithMetadata = new PostWithMetadata[](length);
for (uint256 i = length; i > 0; i--) {
postsWithMetadata[length - i] = _getPostWithMetadata(postIds[postIds.length - cursor - i]);
PostWithMetadata[] memory posts = new PostWithMetadata[](length);

//Do not check when user does not have followings
if ((_type == FeedType.Follow) &&
(EnumerableSet.length(_followConnectionsByUser[msg.sender].following) == 0)) {
return posts;
}
return postsWithMetadata;

uint256 j = 0;
uint256 i = _cursor;

do {
i--;
uint256 id = postIds[i];
if (_includePost(id, _type)) {
posts[j] = _getPostWithMetadata(id);
j++;
}
}
while(i > 0 && j < length);

return posts;
}

function getAllPostsByOwner(address owner)
public
view
returns (PostWithMetadata[] memory)
{
PostWithMetadata[] memory postsWithMetadata = new PostWithMetadata[](postIdsByOwner[owner].length);
for (uint256 i = 0; i < postIdsByOwner[owner].length; i++) {
postsWithMetadata[i] = _getPostWithMetadata(postIdsByOwner[owner][i]);
if (isBlocked(msg.sender, owner) || isBlocked(owner, msg.sender)) {
return new PostWithMetadata[](0);
}
else {
PostWithMetadata[] memory postsWithMetadata = new PostWithMetadata[](postIdsByOwner[owner].length);
for (uint256 i = 0; i < postIdsByOwner[owner].length; i++) {
postsWithMetadata[i] = _getPostWithMetadata(postIdsByOwner[owner][i]);
}
return postsWithMetadata;
}
return postsWithMetadata;
}

function getAllPostsByOwnerLength(address owner)
Expand Down Expand Up @@ -328,7 +413,8 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
post.commentsCount,
getPostDislikesQty(_postId),
checkLikeToPost(_postId),
checkDislikeToPost(_postId)
checkDislikeToPost(_postId),
postIsFlagged[_postId]
);
return postWithMetadata;
}
Expand Down Expand Up @@ -645,6 +731,10 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
return false;
}

/**********************************************************************
* User Profile Functions
***********************************************************************/

function setProfile(
bytes32 name_,
bytes32 location_,
Expand All @@ -664,7 +754,9 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
return profileByOwner[id_];
}

// Data Migrator Functions - only callable by _migrator
/**********************************************************************
* Data Migrator Functions - only callable by _migrator
***********************************************************************/

function add(
uint256 id,
Expand All @@ -673,8 +765,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
bytes32 image,
uint16 likesCount,
uint256 createdAt
) public {
require(msg.sender == _migrator, "Access Denied");
) public onlyMigrator {

Post memory _post = Post({
id: id,
Expand Down Expand Up @@ -706,8 +797,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
address author,
bytes32 contents,
uint256 createdAt
) public {
require(msg.sender == _migrator, "Access Denied");
) public onlyMigrator {

Comment memory _comment = Comment({
id: id,
Expand Down Expand Up @@ -738,8 +828,7 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
bytes32 about,
bytes32 avatar,
bytes32 banner
) public {
require(msg.sender == _migrator, "Access Denied");
) public onlyMigrator {

profileByOwner[user].displayName = name;
profileByOwner[user].displayLocation = location;
Expand All @@ -750,6 +839,85 @@ contract PointSocial is Initializable, UUPSUpgradeable, OwnableUpgradeable, Paus
emit ProfileChange(user, block.timestamp);
}

/**********************************************************************
* Follow functions
***********************************************************************/

modifier onlyMutuals (address _user) {
require(isFollowing(msg.sender, _user), "ERROR_NOT_MUTUAL");
require(isFollowing(_user, msg.sender), "ERROR_NOT_MUTUAL");
_;
}

modifier onlyFollowers (address _user) {
require((msg.sender == _user) || isFollowing(msg.sender, _user), "ERROR_NOT_FOLLOWING");
_;
}

modifier notBlocked (address _user) {
require(!isBlocked(msg.sender, _user), "ERROR_USER_BLOCKED");
require(!isBlocked(_user, msg.sender), "ERROR_USER_BLOCKED");
_;
}

function followUser(address _user) public notBlocked(_user) {
EnumerableSet.add(_followConnectionsByUser[msg.sender].following, _user);
EnumerableSet.add(_followConnectionsByUser[_user].followers, msg.sender);
emit FollowEvent(msg.sender, _user, FollowAction.Follow);
}

function unfollowUser(address _user) public {
EnumerableSet.remove(_followConnectionsByUser[msg.sender].following, _user);
EnumerableSet.remove(_followConnectionsByUser[_user].followers, msg.sender);
emit FollowEvent(msg.sender, _user, FollowAction.UnFollow);
}

function isFollowing(address _owner, address _user) public view returns (bool) {
return EnumerableSet.contains(_followConnectionsByUser[_owner].following, _user);
}

function followingList(address _user) public onlyFollowers(_user) view returns (address[] memory) {
return EnumerableSet.values(_followConnectionsByUser[_user].following);
}

function followingCount(address _user) public view returns (uint256) {
return EnumerableSet.length(_followConnectionsByUser[_user].following);
}

function followersList(address _user) public onlyFollowers(_user) view returns (address[] memory) {
return EnumerableSet.values(_followConnectionsByUser[_user].followers);
}

function followersCount(address _user) public view returns (uint256) {
return EnumerableSet.length(_followConnectionsByUser[_user].followers);
}

function blockUser(address _user) public {
EnumerableSet.remove(_followConnectionsByUser[msg.sender].following, _user);
EnumerableSet.remove(_followConnectionsByUser[msg.sender].followers, _user);
EnumerableSet.remove(_followConnectionsByUser[_user].following, msg.sender);
EnumerableSet.remove(_followConnectionsByUser[_user].followers, msg.sender);
EnumerableSet.add(_followConnectionsByUser[msg.sender].blocked, _user);
emit FollowEvent(msg.sender, _user, FollowAction.Block);
}

function unBlockUser(address _user) public {
EnumerableSet.remove(_followConnectionsByUser[msg.sender].blocked, _user);
emit FollowEvent(msg.sender, _user, FollowAction.UnBlock);
}

function isBlocked(address _owner, address _user) public view returns (bool) {
return EnumerableSet.contains(_followConnectionsByUser[_owner].blocked, _user);
}

function blockList() public view returns (address[] memory) {
return EnumerableSet.values(_followConnectionsByUser[msg.sender].blocked);
}

/**********************************************************************
* Like migration functions
***********************************************************************/

function addLike(
uint256 _id,
address _from,
Expand Down
Loading