[Código-Solidity] Crea tu propio Twitter descentralizado

¿Programar Twitter descentralizado?

Hoy veremos un par de Smart Contracts en Solidity que son básicamente un prototipo de lo que vendría siendo una de las mil maneras de crear tu propio Twitter descentralizado y bueno, utilizando obviamente nuestra hermosa Blockchain de Ethereum. En el post describiré un poco las funcionalidades presentes en el Smart Contract.

El código está escrito en Solidity y podría ser ejecutado en cualquier blockchain que soporte este lenguaje. Consulta la lista de blockchains que permiten Smart Contracts en Solidity aquí

El proyecto se divide en 3 archivos principales (de momento):

  1. Twitter.sol. Contiene funciones administrativas:
    • Bannear usuario (solo el creador del contrato puede bannear usuarios)
    • Desactivar usuarios.
  2. User.sol. Contiene las funciones necesarias para administrar Usuarios:
    • Crear usuario
    • Eliminar usuario (solo el usuario se puede eliminar a si mismo)
    • Reportar usuario
    • Seguir usuario
    • Dejar de seguir usuario
    • Helpers para validar si alguien nos sigue y si seguimos a alguien
  3. Post.sol. Contiene las funciones necesarias para administrar los Posts o Tweets:
    • Crear post/tweet
    • Reportar post
    • Dar me gusta a un post
    • Quitar me gusta a un post

Nota: Recuerda que puedes comenzar tu propia dApp totalmente gratis utilizando la red de pruebas Rinkeby!

Opciones para desplegar Smart Contracts

Al momento de programar Twitter descentralizado, tienes varias opciones para desplegar tus contratos. En este caso se utiliza el framework Embark, por lo que si quieres saber más sobre su funcionamiento te recomiendo pasar directamente a su documentación.

Si no quieres utilizar algún framework como Truffle o Embark, puedes compilar el código en el IDE online oficial de Ethereum. Aprende como hacerlo aquí. El código completo para programar tu app descentralizada lo puedes encontrar en el GitHub oficial de EthereumChile (no olvides dejar tu preciada 🌟❤️). También encontrarás las instrucciones para compilar todo con 4 sencillos comandos:

  • git clone https://github.com/ethereumchile/twitter-descentralizado
  • cd twitter-descentralizado
  • yarn install
  • embark run

Smart Contracts Twitter Descentralizado

Twitter.sol

// SPDX-License-Identifier: GPL-3.0-only
/*
    Author  : Javier Guajardo
    Github  : https://github.com/ethereumchile
    Twitter : @ethereumchile
    Website : https://ethereumchile.cl
    Date    : 03/12/2021
*/

pragma solidity >=0.7.0<=0.9.0;

import "./extensions/User.sol";
import "./extensions/Post.sol";

contract Twitter is PostContract {

    address public admin;

    modifier onlyAdmin {
        require(msg.sender == admin);
        _;
    }

    constructor() {
        admin = msg.sender;
    }

    function deactivatePost(uint number) public onlyAdmin returns (bool) {
        require(post[number].isPublished);
        post[number].isPublished = false;
        return true;
    }

    function banUser(address account) public onlyAdmin returns (bool) {
        require(user[account].isRegistered && !user[account].isBanned);
        user[account].isBanned = true;
        user[account].dateBan = block.timestamp;
        return true;
    }

    function createAccountAsAdmin(address newUser, string memory nickname) public onlyAdmin returns (bool) {
        require(!user[newUser].isRegistered);
        user[newUser].nickname = nickname;
        user[newUser].isRegistered = true;
        user[newUser].dateRegistration = block.timestamp;
        return true;
    }

}

extensions/Post.sol

// SPDX-License-Identifier: GPL-3.0-only
/*
    Author  : Javier Guajardo
    Github  : https://github.com/ethereumchile
    Twitter : @ethereumchile
    Website : https://ethereumchile.cl
    Date    : 03/12/2021
*/

pragma solidity >=0.7.0<=0.9.0;

import "./User.sol";

contract PostContract is UserContract {

    struct Post {
        string title;
        string content;
        uint datePublication;
        bool isPublished;
        address author;
    }

    struct PostReport {
        string reason;
        address author;
        uint postNumber;
        uint date;
    }

    PostReport[] postReports;
    uint postsCounter = 0;
    mapping(uint => Post) public post;
    mapping(uint => mapping(address => bool)) public postLikesCounter;
    mapping(address => mapping(uint => bool)) public postsLikedByUser;

    modifier onlyActivePost(uint number) {
        require(post[number].isPublished, "Post doesn't exist or deactivated");
        _;
    }

    function createPost(string memory title, string memory content) public onlyActiveUser returns (bool) {
        Post memory newPost;
        newPost.title = title;
        newPost.content = content;
        newPost.datePublication = block.timestamp;
        newPost.isPublished = true;
        newPost.author = msg.sender;
        postsCounter++;
        post[postsCounter] = newPost;
        user[msg.sender].postCounter++;
        return true;
    }

    function likePost(uint number) public onlyActiveUser onlyActivePost(number) returns (bool) {
        require(!postsLikedByUser[msg.sender][number]);
        postsLikedByUser[msg.sender][number] = true;
        postLikesCounter[number][msg.sender] = true;
        return true;
    }

    function dislikePost(uint number) public onlyActiveUser onlyActivePost(number) returns (bool) {
        require(postsLikedByUser[msg.sender][number]);
        delete postsLikedByUser[msg.sender][number];
        delete postLikesCounter[number][msg.sender];
        return true;
    }

    function reportPost(uint number, string memory reason) public onlyActiveUser onlyActivePost(number) returns (bool) {
        PostReport memory report;
        report.author = post[number].author;
        report.postNumber = number;
        report.reason = reason;
        postReports.push(report);
        return true;
    }

}

extensions/User.sol

// SPDX-License-Identifier: GPL-3.0-only
/*
    Author  : Javier Guajardo
    Github  : https://github.com/ethereumchile
    Twitter : @ethereumchile
    Website : https://ethereumchile.cl
    Date    : 03/12/2021
*/

pragma solidity >=0.7.0 <=0.9.0;

contract UserContract {
    struct User {
        bool isBanned;
        bool isRegistered;
        string nickname;
        uint256 dateRegistration;
        uint256 dateBan;
        uint256 postCounter;
    }
    struct UserReport {
        string reason;
        uint256 date;
        bool sent;
    }

    mapping(address => User) public user;
    mapping(address => mapping(address => UserReport)) public userReports;
    mapping(address => mapping(address => bool)) public following;
    mapping(address => mapping(address => bool)) public followers;

    modifier onlyActiveUser() {
        require(
            !user[msg.sender].isBanned && user[msg.sender].isRegistered,
            "User doesn't exist or banned"
        );
        _;
    }

    function createAccount(string memory nickname) public returns (bool) {
        require(!user[msg.sender].isRegistered && bytes(nickname).length > 0);
        user[msg.sender].nickname = nickname;
        user[msg.sender].dateRegistration = block.timestamp;
        user[msg.sender].isRegistered = true;
        return true;
    }

    function deleteAccount() public onlyActiveUser returns (bool) {
        delete user[msg.sender];
        return true;
    }

    function followUser(address userAccount)
        public
        onlyActiveUser
        returns (bool)
    {
        require(user[userAccount].isRegistered && !user[userAccount].isBanned);
        following[msg.sender][userAccount] = true;
        followers[userAccount][msg.sender] = true;
        return true;
    }

    function unfollowUser(address userAccount)
        public
        onlyActiveUser
        returns (bool)
    {
        require(user[userAccount].isRegistered && !user[userAccount].isBanned);
        delete following[msg.sender][userAccount];
        return true;
    }

    function checkFollow(address userAccount)
        public
        view
        onlyActiveUser
        returns (bool)
    {
        return following[msg.sender][userAccount];
    }

    function checkFollowme(address userAccount)
        public
        view
        onlyActiveUser
        returns (bool)
    {
        return following[userAccount][msg.sender];
    }

    function reportUser(address userAccount, string memory reason)
        public
        onlyActiveUser
        returns (bool)
    {
        require(
            !userReports[userAccount][msg.sender].sent,
            "You can report a user only once, thank you"
        );
        UserReport memory report;
        report.reason = reason;
        report.date = block.timestamp;
        report.sent = true;
        userReports[userAccount][msg.sender] = report;
        return true;
    }
}

Actualmente me encuentro programando la interfaz gráfica para que sea muchísimo más amigable interactuar con estos Smart Contracts.

Próximo paso: Programar interfaz gráfica

En los siguientes posts usaremos el comando npx create-react-app frontend –template typescript para comenzar a programar nuestra interfaz gráfica y poder interactuar con estos Smart Contracts utilizando la librería Web3.js

Si conoces algún artículo donde también se muestre como programar app descentralizada de este tipo por favor déjalo en los comentarios 🙂

Tienes alguna idea novedosa para este proyecto? Siéntete libre de contribuir en el código enviando tu Pull Request o dejando tu comentario más abajo 🙏🏽

Hasta la próxima!

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada.