From 86afe1acb0e6b01d70b5bf3506ce420e417b0da5 Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Wed, 13 Sep 2023 12:28:12 +0100 Subject: [PATCH 1/9] Deploy the smart contract --- web3_social_media/contracts/SocialMedial.sol | 91 ++++++++++++++++++++ web3_social_media/hardhat.config.js | 10 +++ web3_social_media/package.json | 17 ++++ web3_social_media/scripts/deploy.js | 17 ++++ 4 files changed, 135 insertions(+) create mode 100644 web3_social_media/contracts/SocialMedial.sol create mode 100644 web3_social_media/hardhat.config.js create mode 100644 web3_social_media/package.json create mode 100644 web3_social_media/scripts/deploy.js diff --git a/web3_social_media/contracts/SocialMedial.sol b/web3_social_media/contracts/SocialMedial.sol new file mode 100644 index 00000000..c279ad25 --- /dev/null +++ b/web3_social_media/contracts/SocialMedial.sol @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: MIT +// web3 Social media smart contract +pragma solidity ^0.8.9; + +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; + } + +} \ No newline at end of file diff --git a/web3_social_media/hardhat.config.js b/web3_social_media/hardhat.config.js new file mode 100644 index 00000000..01b1642d --- /dev/null +++ b/web3_social_media/hardhat.config.js @@ -0,0 +1,10 @@ +/** @type import('hardhat/config').HardhatUserConfig */ + +module.exports = { + solidity: "0.8.19", + networks: { + buildbear: { + url: 'https://rpc.buildbear.io/marxist-rugor-nass-cbb8c77f', + }, + } + }; \ No newline at end of file diff --git a/web3_social_media/package.json b/web3_social_media/package.json new file mode 100644 index 00000000..6e7bb64d --- /dev/null +++ b/web3_social_media/package.json @@ -0,0 +1,17 @@ +{ + "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": { + "@nomicfoundation/hardhat-toolbox": "^1.0.2", + "@openzeppelin/contracts": "^4.9.3", + "ethers": "^6.7.1", + "hardhat": "^2.17.3" + } +} diff --git a/web3_social_media/scripts/deploy.js b/web3_social_media/scripts/deploy.js new file mode 100644 index 00000000..5c73b79e --- /dev/null +++ b/web3_social_media/scripts/deploy.js @@ -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); + }); \ No newline at end of file From 523581df5fdfd812d9409edeb9e46b46cf429aec Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Wed, 13 Sep 2023 13:27:56 +0100 Subject: [PATCH 2/9] Solve ethers and hardhat dependency issues --- web3_social_media/README.md | 101 +++++++++++++++++++ web3_social_media/contracts/SocialMedial.sol | 2 +- web3_social_media/hardhat.config.js | 17 ++-- web3_social_media/package.json | 5 +- 4 files changed, 114 insertions(+), 11 deletions(-) create mode 100644 web3_social_media/README.md diff --git a/web3_social_media/README.md b/web3_social_media/README.md new file mode 100644 index 00000000..bc58de4b --- /dev/null +++ b/web3_social_media/README.md @@ -0,0 +1,101 @@ +# Social Media Smart Contract + +## 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 (e.g., Ganache, Ropsten, or the Ethereum Mainnet) with sufficient ETH for transaction fees. +- An Ethereum development environment, such as Remix or a development framework like Truffle. +- Solidity compiler version 0.8.0 or compatible. + +## Contract Details + +- **Solidity Version**: ^0.8.0 +- **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. + +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 owner 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). + +TypeError: Cannot read properties of undefined (reading 'getContractFactory') +require("@nomicfoundation/hardhat-toolbox"); +npm uninstall hardhat; npm install --save-dev hardhat +npm uninstall ethers +npm uninstall @nomicfoundation/hardhat-toolbox; npm install --save-dev @nomicfoundation/hardhat-toolbox@^1.0.2 diff --git a/web3_social_media/contracts/SocialMedial.sol b/web3_social_media/contracts/SocialMedial.sol index c279ad25..ab79d731 100644 --- a/web3_social_media/contracts/SocialMedial.sol +++ b/web3_social_media/contracts/SocialMedial.sol @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT // web3 Social media smart contract -pragma solidity ^0.8.9; +pragma solidity ^0.8.13; contract SocialMedia { diff --git a/web3_social_media/hardhat.config.js b/web3_social_media/hardhat.config.js index 01b1642d..484ed51e 100644 --- a/web3_social_media/hardhat.config.js +++ b/web3_social_media/hardhat.config.js @@ -1,10 +1,11 @@ -/** @type import('hardhat/config').HardhatUserConfig */ +require("@nomicfoundation/hardhat-toolbox"); +/** @type import('hardhat/config').HardhatUserConfig */ module.exports = { - solidity: "0.8.19", - networks: { - buildbear: { - url: 'https://rpc.buildbear.io/marxist-rugor-nass-cbb8c77f', - }, - } - }; \ No newline at end of file + solidity: "0.8.13", + networks: { + buildbear: { + url: "https://rpc.buildbear.io/marxist-rugor-nass-cbb8c77f", + }, + }, +}; \ No newline at end of file diff --git a/web3_social_media/package.json b/web3_social_media/package.json index 6e7bb64d..a13193c4 100644 --- a/web3_social_media/package.json +++ b/web3_social_media/package.json @@ -9,9 +9,10 @@ "author": "", "license": "ISC", "dependencies": { + "@openzeppelin/contracts": "^4.9.3" + }, + "devDependencies": { "@nomicfoundation/hardhat-toolbox": "^1.0.2", - "@openzeppelin/contracts": "^4.9.3", - "ethers": "^6.7.1", "hardhat": "^2.17.3" } } From 51aec694c5ac0314b5f6f5de18972106f0f534fb Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Thu, 14 Sep 2023 11:38:22 +0100 Subject: [PATCH 3/9] Update README.md file --- web3_social_media/README.md | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/web3_social_media/README.md b/web3_social_media/README.md index bc58de4b..442d3758 100644 --- a/web3_social_media/README.md +++ b/web3_social_media/README.md @@ -6,13 +6,13 @@ This Ethereum smart contract functions as a basic social media platform, allowin ## Prerequisites -- An Ethereum wallet and access to a blockchain network (e.g., Ganache, Ropsten, or the Ethereum Mainnet) with sufficient ETH for transaction fees. +- 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.0 or compatible. +- Solidity compiler version 0.8.13 or compatible. ## Contract Details -- **Solidity Version**: ^0.8.0 +- **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 @@ -66,6 +66,8 @@ This Ethereum smart contract functions as a basic social media platform, allowin 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. @@ -93,9 +95,3 @@ This Ethereum smart contract functions as a basic social media platform, allowin ## License This smart contract is released under the [MIT License](LICENSE). - -TypeError: Cannot read properties of undefined (reading 'getContractFactory') -require("@nomicfoundation/hardhat-toolbox"); -npm uninstall hardhat; npm install --save-dev hardhat -npm uninstall ethers -npm uninstall @nomicfoundation/hardhat-toolbox; npm install --save-dev @nomicfoundation/hardhat-toolbox@^1.0.2 From 84d9ed155d1b4d8f3d76507da5a090a3b0d3944c Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Thu, 14 Sep 2023 11:43:28 +0100 Subject: [PATCH 4/9] Update README.md file --- web3_social_media/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web3_social_media/README.md b/web3_social_media/README.md index 442d3758..901d6c03 100644 --- a/web3_social_media/README.md +++ b/web3_social_media/README.md @@ -1,5 +1,5 @@ # 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. From 8d213c7ff192d0d045f1ce7bbf25fc50880eb8b8 Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Wed, 22 Nov 2023 12:03:49 +0100 Subject: [PATCH 5/9] Pull request to merge web3 social media --- web3_social_media/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web3_social_media/README.md b/web3_social_media/README.md index 901d6c03..b0a96d5b 100644 --- a/web3_social_media/README.md +++ b/web3_social_media/README.md @@ -90,7 +90,7 @@ This Ethereum smart contract functions as a basic social media platform, allowin ## Disclaimer -- This smart contract is provided as-is. The owner and developers are not responsible for any issues, bugs, or misuse that may occur while using this contract. +- 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 From 042668ac73bdddd1db9d5dd7b8dc1a8e748893f3 Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Sat, 2 Dec 2023 17:10:15 +0100 Subject: [PATCH 6/9] Test to the smart contract --- web3_social_media/scripts/test/ocialMedia.test.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 web3_social_media/scripts/test/ocialMedia.test.js diff --git a/web3_social_media/scripts/test/ocialMedia.test.js b/web3_social_media/scripts/test/ocialMedia.test.js new file mode 100644 index 00000000..e69de29b From 38b3603c4f5057f76c1739dc3a80b3cb84bcd38a Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Sat, 2 Dec 2023 17:16:26 +0100 Subject: [PATCH 7/9] Revert " Test to the smart contract" This reverts commit 042668ac73bdddd1db9d5dd7b8dc1a8e748893f3. --- web3_social_media/scripts/test/ocialMedia.test.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 web3_social_media/scripts/test/ocialMedia.test.js diff --git a/web3_social_media/scripts/test/ocialMedia.test.js b/web3_social_media/scripts/test/ocialMedia.test.js deleted file mode 100644 index e69de29b..00000000 From f0d24bc6368a78186d3f5d6dc7e68fa26442b8b1 Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Sat, 2 Dec 2023 17:19:09 +0100 Subject: [PATCH 8/9] Add test to the smart contact --- .../scripts/test/socialMedia.test.js | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 web3_social_media/scripts/test/socialMedia.test.js diff --git a/web3_social_media/scripts/test/socialMedia.test.js b/web3_social_media/scripts/test/socialMedia.test.js new file mode 100644 index 00000000..369670df --- /dev/null +++ b/web3_social_media/scripts/test/socialMedia.test.js @@ -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"); + }); +}); From 94408289cd8c97d71c11ec27bfd9cd4fc27f89ca Mon Sep 17 00:00:00 2001 From: AYcrown77 Date: Sat, 2 Dec 2023 17:28:33 +0100 Subject: [PATCH 9/9] Include untracked test file --- web3_social_media/test/socialMedia.test.js | 48 ++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 web3_social_media/test/socialMedia.test.js diff --git a/web3_social_media/test/socialMedia.test.js b/web3_social_media/test/socialMedia.test.js new file mode 100644 index 00000000..369670df --- /dev/null +++ b/web3_social_media/test/socialMedia.test.js @@ -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"); + }); +});