Mi Primera WebApp en Web3

JSDay Canarias 2022 - Antonio Sejas

Mi Primera DApp

JSDay Canarias 2022 - Antonio Sejas

Sobre mi

Software Engineer

Antonio Sejas

Objetivo del taller

Objetivo del taller

Objetivo del taller

Ante el inminente derrumbe de twitter, tras su adquisición por parte de Elon Musk.

 

Os propongo que hagamos un "Clon" de Twitter basado en Blockchain

Objetivo del taller

Ante el inminente derrumbe de twitter, tras su adquisición por parte de Elon Musk.

 

Os propongo que hagamos un "Clon" de Twitter basado en Blockchain

 

Canary DApp

Objetivo del taller

Índice

  • Requisitos
  • WEB 3.0
  • Desarrollo de la DApp en React  (Front)
  • Desplegar nuestro SmartContract (Back)
  • Siguientes pasos

Índice

1

Requisitos

2

WEB 3

3

Smart Contract (Back)

5

Conclusiones

4

DApp en React (Front)

Requisitos

Web 3.0

¿Qué es la WEB 3.0?

¿Qué es la WEB 3.0?

Disclaimer

This is not a financial advice

¿Qué es la WEB 3.0?

¿Qué es la WEB 3.0?

Satoshi Nakamoto introduce en el primer bloque minado de bitcoin el titular del periódico ‘The Times’ del 3 de enero de 2009. Es una prueba sobre la fecha de creación de este bloque.

“El Canciller (británico) está considerando el segundo rescate a los bancos”

Blockchain

Cadena de bloques

Blockchain

Minado de transacciones

Blockchain

Minado de transacciones

Wallet

  • Combinación e clave pública privada. PKI
  • Muy similar a nuestro DNIe
  • Nos autentica.

 

Minar

  • Confirma transacciones
  • Recompensas

Blockchain

Mecanismos de consenso

Smartcontracts

Blockchains

Smartcontracts

Ethereum

Nosotros desplegaremos en la testnet Rinkeby

Smartcontracts

EVM

Smartcontracts

Solidity

pragma solidity ^0.8.13;
contract HelloWorld {
   string public message;
   constructor(string memory initMessage) {
      message = initMessage;
   }
   function update(string memory newMessage) public {
      message = newMessage;
   }
}

Smartcontracts

Inspeccionando en etherscan

Demo

Tokens

ERC-20

ERC, Ethereum Request for Comments.

Medio de pago.

Gobernanza de DAOs, decentralized autonomous organization.

Non-fungible tokens (NFT)

ERC-721

Asset digital identificable de forma inequívoca

Coleccionable

Certificado de "propiedad"

Tarjeta de fidelidad, pertenencia a un club/comunidad

Non-fungible tokens (NFT)

Non-fungible tokens (NFT)

Non-fungible tokens (NFT)

Dapps

JSON-RPC API

Dapps

Dapps

Deploy del Smartcontract

Deploy del Smartcontract

https://faucets.chain.link/rinkeby
 
¿Qué pasa si ya tengo una wallet?
Crea otra, no vaya a ser que tengas un virus, publiques tu clave privada por error o despliegues en pro por un ojo de la cara.

Deploy del Smartcontract

Deploy del Smartcontract

Deploy con Hardhat

CONTRACT_NAME=Canary npx hardhat run scripts/deploy.js --network rinkeby

❯ CONTRACT_NAME=Canary npx hardhat run scripts/deploy.js --network rinkeby
Compiled 4 Solidity files successfully
Deploying contracts with account:  0xeA39B449Ea509b0957c0F23fa0703a800DbA1bfB
Account balance:  100000000000000000
Contract address:  0xd44aBFB64B98C13a69839BFF433814446726703A

Deploy del Smartcontract

// npx hardhat node
// CONTRACT_NAME=Counter npx hardhat run scripts/deploy.js --network localhost

const main = async (contractName) => {
  const [deployer] = await hre.ethers.getSigners();
  const accountBalance = await deployer.getBalance();

  console.log("Deploying contracts with account: ", deployer.address);
  console.log("Account balance: ", accountBalance.toString());

  const contractFactory = await hre.ethers.getContractFactory(contractName);
  const contract = await contractFactory.deploy();
  await contract.deployed();

  console.log("Contract address: ", contract.address);
};

const runMain = async (contractName) => {
  try {
    await main(contractName);
    process.exit(0);
  } catch (error) {
    console.log(error);
    process.exit(1);
  }
};

const contractName = process.env.CONTRACT_NAME || "Counter";
runMain(contractName);

deploy.js

Deploy del Smartcontract

// SPDX-License-Identifier: UNLICENSED

pragma solidity ^0.8.0;

import "hardhat/console.sol";

contract Counter {
    uint256 total;

    constructor() {
        console.log("Couner initialized");
    }

    function add() public {
        total += 1;
        console.log("%s has added 1 the new total is %s", msg.sender, total);
    }

    function getTotal() public view returns (uint256) {
        console.log("The total is %d!", total);
        return total;
    }
}

Counter.sol

Deploy del Smartcontract

// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.13;

contract Canary {
    event AddTweet(address recipient, uint256 tweetId);
    event DeleteTweet(uint256 tweetId, bool isDeleted);
    event LikedTweet(address enthusiast, uint256 tweetId);

    struct Tweet {
        uint256 id;
        address owner;
        string tweetText;
        bool isDeleted;
        uint32 likes;
        uint256 timestamp;
    }

    Tweet[] private tweets;

    // Mapping of Tweet id to the wallet address of the user
    mapping(uint256 => address) tweetToOwner;
    // mapping(uint256 => address[]) tweetToLikes;
    mapping(address => uint256[]) addressToTweetsLiked;

    // Method to be called by our frontend when trying to add a new Tweet
    function addTweet(string memory tweetText) external {
        uint256 tweetId = tweets.length;
        tweets.push(
            Tweet(tweetId, msg.sender, tweetText, false, 0, block.timestamp)
        );
        tweetToOwner[tweetId] = msg.sender;
        emit AddTweet(msg.sender, tweetId);
    }

    // Method to get one single Tweet
    function getTweet(uint256 tweetId) external view returns (Tweet memory) {
        return tweets[tweetId];
    }

    // Method to get all the Tweets
    function getAllTweets() external view returns (Tweet[] memory) {
        Tweet[] memory temporary = new Tweet[](tweets.length);
        uint256 counter = 0;
        for (uint256 i = 0; i < tweets.length; i++) {
            if (tweets[i].isDeleted == false) {
                temporary[counter] = tweets[i];
                counter++;
            }
        }

        Tweet[] memory result = new Tweet[](counter);
        for (uint256 i = 0; i < counter; i++) {
            result[i] = temporary[i];
        }
        return result;
    }

    // Method to get only your Tweets
    function getMyTweets() external view returns (Tweet[] memory) {
        Tweet[] memory temporary = new Tweet[](tweets.length);
        uint256 counter = 0;
        for (uint256 i = 0; i < tweets.length; i++) {
            if (tweetToOwner[i] == msg.sender && tweets[i].isDeleted == false) {
                temporary[counter] = tweets[i];
                counter++;
            }
        }

        Tweet[] memory result = new Tweet[](counter);
        for (uint256 i = 0; i < counter; i++) {
            result[i] = temporary[i];
        }
        return result;
    }

    // Method to get only your Tweets
    function getMyLikes() external view returns (Tweet[] memory) {
        uint256[] memory tweetsLiked = addressToTweetsLiked[msg.sender];
        Tweet[] memory result = new Tweet[](tweetsLiked.length);
        uint256 counter = 0;
        for (uint256 i = 0; i < tweetsLiked.length; i++) {
            result[counter] = tweets[tweetsLiked[i]];
            counter++;
        }
        return result;
    }

    // Like tweet
    function likeTweet(uint256 tweetId) external {
        uint256[] memory tweetsLiked = addressToTweetsLiked[msg.sender];
        for (uint256 i = 0; i < tweetsLiked.length; i++) {
            if (tweetsLiked[i] == tweetId) {
                revert("You have already liked this tweet");
            }
        }
        tweets[tweetId].likes = tweets[tweetId].likes + 1;
        // tweetToLikes[tweetId].push(msg.sender);
        addressToTweetsLiked[msg.sender].push(tweetId);
        emit LikedTweet(msg.sender, tweetId);
    }

    // Method to Delete a Tweet
    function deleteTweet(uint256 tweetId, bool isDeleted) external {
        if (tweetToOwner[tweetId] == msg.sender) {
            tweets[tweetId].isDeleted = isDeleted;
            emit DeleteTweet(tweetId, isDeleted);
        }
    }
}

Canary.sol

DApp en React

DApp en React

DApp en React

Creando el repo
 
 

DApp en React

Extra:

- Enseñar la red actual junto a la cuenta

- Añadir pestañas para mostrar mis likes

- Modificar el smart contract para poder añadir pestaña para mostrar mis tweets borrados y sacarlos de la papelera.

- Además de Likes, permitir Donaciones

DApp en React

Ethereum API Provider en Metamask
 
 
import { useEffect, useState } from 'react';

const VALID_CHAIN_ID = '0x4'; // Rinkeby

export function useAccount(ethereum) {
  const [currentAccount, setCurrentAccount] = useState('');
  const [chainId, setChainId] = useState(null);
  useEffect(() => {
    if (!ethereum) {
      return;
    }
    function listenAccount (accounts) {
      console.log('accounts', accounts);
      if (accounts.length > 0) {
        setCurrentAccount(accounts[0]);
      } else {
        setCurrentAccount('');
      }
    }
    function listenChain(chainId) {
      // https://chainlist.org/
      setChainId(chainId);
    }
      
    ethereum.on('accountsChanged', listenAccount);
    ethereum.on('chainChanged', listenChain);
      
    return () => {
      ethereum.removeListener('accountsChanged', listenAccount);
      ethereum.removeListener('chainChanged', listenChain);
    };
  }, [ethereum]);
  return {currentAccount, chainId, setCurrentAccount, setChainId, isValidChain: chainId === VALID_CHAIN_ID};
}

DApp en React

Ethereum API Provider en Metamask
 
 
import { ethers } from 'ethers';
import { useEffect, useState } from 'react';
import ContractJson from '../utils/Canary.json';

const CanaryDetails = {
  CONTRACT_ADDRESS: '0xd44aBFB64B98C13a69839BFF433814446726703A',
  CONTRACT_ABI: ContractJson.abi,
};  

export function useContract(ethereum, isValidChain) {
  const [contract, setContract] = useState(null);
  useEffect(() => {
    if (!ethereum || !isValidChain || contract) {
      return;
    }
    // We initialize the contract with the provider and the ABI only once
    const provider = new ethers.providers.Web3Provider(ethereum);
    const signer = provider.getSigner();
    const newContract = new ethers.Contract(CanaryDetails.CONTRACT_ADDRESS, CanaryDetails.CONTRACT_ABI, signer);
    setContract(newContract);
  }, [ethereum, contract, isValidChain]);
  return contract;
}

Conclusiones

Conclusiones

El blockchain es un ecosistema de DApps, DAOS

 

Que busca evitar la manipulación respetando la privacidad del usuario y la descentralización. Surge como respuesta a la crisis del 2008.

 

Los gobiernos están creando sus propias cryptomonedas oficiales.

 

IPFS para desplegar Actualización de Smart Contracts ¿Son inmutables?

 

La tecnología está para quedarse

Web3

By sejas

Web3

Taller mi primera aplicación web3 + React. JSDay Canarias 2022

  • 285