A social network built on the Celo blockchain that follows a decentralized model and incentivizes users for their contributions and engagement

A social network built on the Celo blockchain that follows a decentralized model and incentivizes users for their contributions and engagement https://celo.academy/uploads/default/optimized/2X/d/d090eb2489c77275df9aa80a62f6ceb614604622_2_1024x576.jpeg
none 0.0 0

Introduction

This project is a decentralized social network built on the Celo blockchain. It allows users to create and share posts with a reward system based on the number of likes received. The platform uses smart contracts to ensure transparency and security. Users can like posts created by others, earning them rewards in the form of CELO tokens. The tutorial will guide users on how to interact with the contract, create posts, like posts, and retrieve information about posts.

Requirements

To follow this tutorial, you will require:

  • A code editor or text editor such as Remix.

  • An internet browser and a stable internet connection.

Prerequisites

To successfully complete this tutorial, it is recommended that you have:

  • Familiarity with Javascript programming language.

  • A basic understanding of Blockchain technology and its functioning.

  • Basic knowledge of the Solidity programming language used for smart contract development on the blockchain.

We will begin by using the Remix IDE to write our smart contract. Let’s get started!

Click here to get the complete code for this session

The complete code:

// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";


interface IERC20Token {
   function transfer(address, uint256) external returns (bool);

   function approve(address, uint256) external returns (bool);

   function transferFrom(
       address,
       address,
       uint256
   ) external returns (bool);

   function totalSupply() external view returns (uint256);

   function balanceOf(address) external view returns (uint256);

   function allowance(address, address) external view returns (uint256);

   event Transfer(address indexed from, address indexed to, uint256 value);
   event Approval(
       address indexed owner,
       address indexed spender,
       uint256 value
   );
}

contract DecentralizedSocialNetwork {
   using SafeMath for uint256;

   struct Post {
       address payable owner;
       string content;
       uint256 likes;
       uint256 rewards;
   }

   uint256 internal postCount = 0;
   address internal cUsdTokenAddress = 0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1;
   uint256 internal rewardPerLike = 1e18; // 1 CELO

   mapping(uint256 => Post) internal posts;

   function createPost(string memory _content) public {
       Post storage newPost = posts[postCount];
       newPost.owner = payable(msg.sender);
       newPost.content = _content;
       newPost.likes = 0;
       newPost.rewards = 0;

       postCount++;
   }

   function getPost(uint256 _postId) public view returns (
       address payable,
       string memory,
       uint256,
       uint256
   ) {
       require(_postId < postCount, "Invalid post ID");

       Post memory post = posts[_postId];
       return (
           post.owner,
           post.content,
           post.likes,
           post.rewards
       );
   }

   function likePost(uint256 _postId) public {
       require(_postId < postCount, "Invalid post ID");

       Post storage post = posts[_postId];
       require(post.owner != msg.sender, "You cannot like your own post");

       post.likes = post.likes.add(1);
       post.rewards = post.rewards.add(rewardPerLike);

       IERC20Token(cUsdTokenAddress).transferFrom(
           msg.sender,
           post.owner,
           rewardPerLike
       );
   }

   function getPostCount() public view returns (uint256) {
       return postCount;
   }
}

Analysing the code

 // SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";

To begin, we start by specifying the license for the code using the MIT license, which grants permission for various use cases.

Next, we use the pragma statement to specify the version of Solidity that the code is compatible with. This ensures that the code is compiled using the correct Solidity version, avoiding any potential compatibility issues.

We then import the SafeMath library from the OpenZeppelin contract library. The SafeMath library provides secure arithmetic operations to prevent common vulnerabilities like overflow and underflow in our contract. By using SafeMath, we can perform mathematical operations safely and avoid potential security risks.

interface IERC20Token {
    function transfer(address, uint256) external returns (bool);

    function approve(address, uint256) external returns (bool);

    function transferFrom(
        address,
        address,
        uint256
    ) external returns (bool);

    function totalSupply() external view returns (uint256);

    function balanceOf(address) external view returns (uint256);

    function allowance(address, address) external view returns (uint256);

    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );
}

In this session, we have an interface called IERC20Token that we can use as a blueprint for implementing ERC-20 token contracts. The interface defines a set of functions and events that allow us to interact with ERC-20 tokens.

By implementing this interface in our own contract, we ensure that our contract is compatible with other contracts and systems that interact with ERC-20 tokens. We can use the defined functions, such as transfer, approve, and balanceOf, to perform token transfers, approvals, and balance inquiries.

The events defined in the interface, such as Transfer and Approval, allow us to emit events when token transfers or approvals occur. This enables other contracts and external systems to track and react to these events.

Using the IERC20Token interface, we can create contracts that seamlessly interact with ERC-20 tokens, making our contracts more interoperable and compatible with the wider Ethereum ecosystem.

contract DecentralizedSocialNetwork {
    using SafeMath for uint256;

    struct Post {
        address payable owner;
        string content;
        uint256 likes;
        uint256 rewards;
    }

    uint256 internal postCount = 0;
    address internal cUsdTokenAddress = 0x874069Fa1Eb16D44d622F2e0Ca25eeA172369bC1;
    uint256 internal rewardPerLike = 1e18; // 1 CELO

    mapping(uint256 => Post) internal posts;

In this session, we will create a contract called DecentralizedSocialNetwork. This contract represents a decentralized social network on the Celo blockchain, which we aim to build.

Within the contract, we have defined a struct called Post. This struct represents the structure of each post in our social network. Each post consists of properties such as owner, content, likes, and rewards.

To keep track of the posts, we have an internal variable called postCount. This variable helps us count the number of posts that have been created so far. Additionally, we store the address of the CELO-based token used for rewards in the cUsdTokenAddress variable. The rewardPerLike variable determines the amount of reward given for each like, represented in CELO tokens.

To store and access posts, we use a mapping called posts. This mapping allows us to associate a post ID with its corresponding Post struct.

To add more functionality to our smart contract, we will be implementing various functions.

function createPost(string memory _content) public {
        Post storage newPost = posts[postCount];
        newPost.owner = payable(msg.sender);
        newPost.content = _content;
        newPost.likes = 0;
        newPost.rewards = 0;

        postCount++;
    }

To allow users to create posts in our decentralized social network, we need to implement the createPost function.

The createPost function takes a parameter _content, which represents the content of the post that the user wants to create.

Inside the function, we create a new Post instance and store it in the posts mapping using the postCount as the key. We set the properties of the new post, including the owner (set as the address of the sender), the content, likes (initially set to 0), and rewards (initially set to 0).

After setting the properties, we increment the postCount variable to keep track of the number of posts created.

By implementing this function, users can create posts in our decentralized social network by providing the content of the post they want to share.

 function getPost(uint256 _postId) public view returns (
        address payable,
        string memory,
        uint256,
        uint256
    ) {
        require(_postId < postCount, "Invalid post ID");

        Post memory post = posts[_postId];
        return (
            post.owner,
            post.content,
            post.likes,
            post.rewards
        );
    }

To enable users to retrieve information about a specific post, we implement the getPost function.

The getPost function takes a parameter _postId, which represents the ID of the post we want to retrieve.

Inside the function, we first validate that the provided _postId is valid by checking if it is less than the postCount variable. If the validation fails, it throws an error message.

If the _postId is valid, we retrieve the corresponding post from the posts mapping using the _postId as the key. We store the post in memory using the Post struct type.

Finally, we return the properties of the post, including the owner address (as a payable address), the content of the post, the number of likes, and the accumulated rewards.

By implementing this function, users can view the details of a specific post by providing its ID.

   function likePost(uint256 _postId) public {
        require(_postId < postCount, "Invalid post ID");

        Post storage post = posts[_postId];
        require(post.owner != msg.sender, "You cannot like your own post");

        post.likes = post.likes.add(1);
        post.rewards = post.rewards.add(rewardPerLike);

        IERC20Token(cUsdTokenAddress).transferFrom(
            msg.sender,
            post.owner,
            rewardPerLike
        );
    }

To allow users to like posts and earn rewards, we implement the likePost function.

The likePost function takes a parameter _postId, representing the ID of the post that the user wants to like.

Inside the function, we first validate that the provided _postId is valid by checking if it is less than the postCount variable. If the validation fails, it throws an error message.

Next, we retrieve the post from the posts mapping using the _postId as the key. We use the storage keyword to ensure that we are modifying the actual post in storage.

We also check that the sender of the like is not the owner of the post. If the validation fails, it throws an error message.

If the validations pass, we increment the likes count of the post by 1 and increase the rewards of the post by the rewardPerLike value.

Finally, we use the transferFrom function of the IERC20Token interface to transfer the reward tokens (rewardPerLike) from the sender (msg.sender) to the owner of the post (post.owner).

By implementing this function, users can like posts, which increases the likes count and rewards the post owner with the specified reward amount.

function getPostCount() public view returns (uint256) {
        return postCount;
    }
}

You can access the full code by clicking this Link

To provide users with information about the total number of posts in our decentralized social network, we implement the getPostCount function.

The getPostCount function does not require any parameters. It simply returns the value of the postCount variable, which keeps track of the number of posts created.

By implementing this function, users can easily retrieve the total count of posts in our social network. This information can be useful for displaying statistics or providing an overview of the network’s activity.

Contract Deployment

In order to deploy the SocialNetwork smart contract on the Celo blockchain, several things are required such as:

To ensure a smooth deployment of our smart contract, it is essential to download the Celo extension wallet from the given link, Celo Extension wallet. Once done, the next step is to fund the wallet that we have created, Celo faucet. This can be accomplished by accessing the Celo Alfojares faucet using the provided link.

With our wallet funded, we can now proceed to deploy the smart contract using the Celo plugin available in Remix.

Conclusion

In this tutorial, we have built a decentralized social network on the Celo blockchain using Solidity. We covered the creation of posts, retrieving post information, liking posts, distributing rewards, and getting the total post count. By leveraging smart contracts and the transparency of blockchain technology, we created a secure environment for users to interact and share content. This tutorial provides a foundation for building decentralized applications and empowers you to explore the possibilities of blockchain technology.

Next Steps

Great job! It’s always helpful to provide additional resources for further learning. Don’t hesitate to reach out if you have any more questions.

About the author

My name is Ogoyi Thompson, and I’m a web3 developer based in Nigeria. I am enthusiastic about working with blockchain technology. If you need further assistance, you can reach out to me on twitter by clicking this link. Happy learning!

5 Likes

I’ll be reviewing this @thompsonogoyi1t

1 Like

Please use an appropriate title case for all
Your headers
Change from “Prerequisite” to “Prerequisites” and “Requirement” to “Requirements”
Provide your social media handle in the about us section

Put your contract names in code boxes

Should be Next Steps

Thanks

2 Likes

done making changes please check

1 Like

Looks good. You can move to publish

1 Like

@thompsonogoyi1t Kudos, I liked this piece, but you have to attach the source code link , please move this card back in publish section after making the changes and then you can tag me and let me know that you have make the changes.

Thank You
Ishan

2 Likes

thank you for the feedback ishan, i have added the github repo

2 Likes

Nice tutorial :100:

3 Likes

Great work @thompsonogoyi1t