Skip to content

Web3 social media project #61

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
97 changes: 97 additions & 0 deletions web3_social_media/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# Social Media Smart Contract
- Note: This project include an [article](https://medium.com/@aycrown77/resolving-a-getcontractfactory-error-in-hardhat-a-step-by-step-guide-d901fc186815) on how I was able to resolve the deployment error I was facing.
## Overview

This Ethereum smart contract functions as a basic social media platform, allowing users to create posts, like posts created by others, and view their own and other users' posts.

## Prerequisites

- An Ethereum wallet and access to a blockchain network (i.e Buildbear Etherium testnet) with sufficient ETH for transaction fees.
- An Ethereum development environment, such as Remix or a development framework like Truffle.
- Solidity compiler version 0.8.13 or compatible.

## Contract Details

- **Solidity Version**: ^0.8.13
- **Owner**: The owner of this contract is set at deployment and is the Ethereum address that deploys it. The owner has special privileges, such as changing the maximum post length.

## Features

### 1. Create Post

- **Function**: `createPost(string memory _post)`
- Allows a user to create a new post.
- Verifies that the post length does not exceed the maximum allowed length (`MAX_POST_LENGHT`).
- Emits a `postCreated` event upon successful post creation.

### 2. Like Post

- **Function**: `likeTweet(address author, uint256 id) external`
- Allows a user to like a post created by another user.
- Emits a `postLiked` event upon successful liking.

### 3. Unlike Post

- **Function**: `unlikeTweet(address author, uint256 id) external`
- Allows a user to unlike a post they had previously liked.
- Emits a `postUnliked` event upon successful unliking.

### 4. Get Post

- **Function**: `getPost(uint _i) public view returns (Post memory)`
- Retrieves a specific post created by the calling user.

### 5. Get All Posts

- **Function**: `getAllPost(address _owner) public view returns (Post[] memory)`
- Retrieves all posts created by a specific user.

### 6. Change Post Length

- **Function**: `changePostLength(uint16 newPostLength) public onlyOwner`
- Allows the owner to change the maximum allowed post length.

## Events

- `postCreated(uint256 id, address author, string content, uint256 timestamp)`
- Emitted when a new post is created.

- `postLiked(address liker, address postAuthor, uint256 postId, uint256 newLikeCount)`
- Emitted when a post is liked.

- `postUnliked(address unliker, address postAuthor, uint256 postId, uint256 newLikedCount)`
- Emitted when a like on a post is removed.

## Usage

1. **Deployment**
- Deploy the contract using a compatible Ethereum development environment.
- Due to the issues I faced while trying to deploy my smart contract, I wrote an article on how I was able to overcome the challenge
- Here is the link to the article: https://medium.com/@aycrown77/resolving-a-getcontractfactory-error-in-hardhat-a-step-by-step-guide-d901fc186815

2. **Creating a Post**
- Use the `createPost` function to create a new post.

3. **Liking a Post**
- Use the `likeTweet` function, providing the author's address and the post ID.

4. **Unliking a Post**
- Use the `unlikeTweet` function, providing the author's address and the post ID.

5. **Viewing Posts**
- Use the `getPost` or `getAllPost` functions to view posts.

6. **Changing Post Length (Owner only)**
- Use the `changePostLength` function to adjust the maximum post length.

## Notes

- Ensure that the maximum post length (`MAX_POST_LENGHT`) is set to a reasonable value to prevent excessive gas costs for long posts.

## Disclaimer

- This smart contract is provided as-is. The owners and developers are not responsible for any issues, bugs, or misuse that may occur while using this contract.

## License

This smart contract is released under the [MIT License](LICENSE).
91 changes: 91 additions & 0 deletions web3_social_media/contracts/SocialMedial.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: MIT
// web3 Social media smart contract
pragma solidity ^0.8.13;

contract SocialMedia {

uint16 public MAX_POST_LENGHT = 200;
address public owner;

// define Post struct
struct Post {
uint256 id;
address author;
string content;
uint256 timestamp;
uint256 likes;
}
// maps user to post
mapping(address => Post[]) public posts;

// Define the events
event postCreated(uint256 id, address author,
string content, uint256 timestamp);
event postLiked(address liker, address postAuthor,
uint256 postId, uint256 newLikeCount);
event postUnliked(address unliker, address postAuthor,
uint256 postId, uint256 newLikedCount);

//constructor to set an owner of the contract
constructor() {
owner = msg.sender;
}

//Modifier to verify the real owner
modifier onlyOwner() {
require(msg.sender == owner, "YOU ARE NOT THE OWNER OF THE CONTRACT");
_;
}

// Adds a post to the user
function createPost(string memory _post) public {
//check if post length <= 200, continue, otherwise revert
require(bytes(_post).length <= MAX_POST_LENGHT, "Post too long!");
Post memory newPost = Post({
id: posts[msg.sender].length,
author: msg.sender,
content: _post,
timestamp: block.timestamp,
likes: 0
});
posts[msg.sender].push(newPost);

emit postCreated(newPost.id, newPost.author, newPost.content, newPost.timestamp);
}

// like a post
function likeTweet(address author, uint256 id) external {
require(posts[author][id].id == id, "post does not exist");
posts[author][id].likes++;
uint256 newLikeCount = posts[author][id].likes;

emit postLiked(msg.sender, author, id, newLikeCount);
}

// Unlike the post
function unlikeTweet(address author, uint256 id) external {
require(posts[author][id].id == id, "Post does not exist");
require(posts[author][id].likes > 0, "Post has no likes");

posts[author][id].likes--;

uint256 newLikeCount = posts[author][id].likes;
emit postUnliked(msg.sender, author, id, newLikeCount);
}

// Gets post from the user
function getPost(uint _i) public view returns (Post memory){
return posts[msg.sender][_i];
}

// Gets all post from the user
function getAllPost(address _owner) public view returns (Post[] memory){
return posts[_owner];
}

// Changes the length of post
function changePostLength(uint16 newPostLength) public onlyOwner{
MAX_POST_LENGHT = newPostLength;
}

}
11 changes: 11 additions & 0 deletions web3_social_media/hardhat.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
solidity: "0.8.13",
networks: {
buildbear: {
url: "https://rpc.buildbear.io/marxist-rugor-nass-cbb8c77f",
},
},
};
18 changes: 18 additions & 0 deletions web3_social_media/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "web3_social_media",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"@openzeppelin/contracts": "^4.9.3"
},
"devDependencies": {
"@nomicfoundation/hardhat-toolbox": "^1.0.2",
"hardhat": "^2.17.3"
}
}
17 changes: 17 additions & 0 deletions web3_social_media/scripts/deploy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const hre = require("hardhat");

async function main() {
const SocialMedia = await hre.ethers.getContractFactory("SocialMedia");
const socialMedia = await SocialMedia.deploy();

await socialMedia.deployed();

console.log("SocialMedia deployed to:", socialMedia.address);
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
48 changes: 48 additions & 0 deletions web3_social_media/scripts/test/socialMedia.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const SocialMedia = artifacts.require("SocialMedia");

contract("SocialMedia", (accounts) => {
let socialMediaInstance;

beforeEach(async () => {
socialMediaInstance = await SocialMedia.new();
});

it("should create a post", async () => {
const content = "This is a test post.";

await socialMediaInstance.createPost(content);

const post = await socialMediaInstance.getPost(0);
assert.equal(post.content, content, "Post content does not match");
});

it("should like a post", async () => {
const content = "This is another test post.";
await socialMediaInstance.createPost(content);

await socialMediaInstance.likeTweet(accounts[0], 0);

const post = await socialMediaInstance.getPost(0);
assert.equal(post.likes, 1, "Post should have 1 like");
});

it("should unlike a post", async () => {
const content = "Yet another test post.";
await socialMediaInstance.createPost(content);

await socialMediaInstance.likeTweet(accounts[0], 0);
await socialMediaInstance.unlikeTweet(accounts[0], 0);

const post = await socialMediaInstance.getPost(0);
assert.equal(post.likes, 0, "Post should have 0 likes");
});

it("should change post length", async () => {
const newPostLength = 300;

await socialMediaInstance.changePostLength(newPostLength);

const updatedPostLength = await socialMediaInstance.MAX_POST_LENGHT();
assert.equal(updatedPostLength, newPostLength, "Post length should be updated");
});
});
48 changes: 48 additions & 0 deletions web3_social_media/test/socialMedia.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const SocialMedia = artifacts.require("SocialMedia");

contract("SocialMedia", (accounts) => {
let socialMediaInstance;

beforeEach(async () => {
socialMediaInstance = await SocialMedia.new();
});

it("should create a post", async () => {
const content = "This is a test post.";

await socialMediaInstance.createPost(content);

const post = await socialMediaInstance.getPost(0);
assert.equal(post.content, content, "Post content does not match");
});

it("should like a post", async () => {
const content = "This is another test post.";
await socialMediaInstance.createPost(content);

await socialMediaInstance.likeTweet(accounts[0], 0);

const post = await socialMediaInstance.getPost(0);
assert.equal(post.likes, 1, "Post should have 1 like");
});

it("should unlike a post", async () => {
const content = "Yet another test post.";
await socialMediaInstance.createPost(content);

await socialMediaInstance.likeTweet(accounts[0], 0);
await socialMediaInstance.unlikeTweet(accounts[0], 0);

const post = await socialMediaInstance.getPost(0);
assert.equal(post.likes, 0, "Post should have 0 likes");
});

it("should change post length", async () => {
const newPostLength = 300;

await socialMediaInstance.changePostLength(newPostLength);

const updatedPostLength = await socialMediaInstance.MAX_POST_LENGHT();
assert.equal(updatedPostLength, newPostLength, "Post length should be updated");
});
});