JSDay Canarias 2022 - Antonio Sejas
JSDay Canarias 2022 - Antonio Sejas
Software Engineer
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.
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
Requisitos
WEB 3
Smart Contract (Back)
Conclusiones
DApp en React (Front)
Disclaimer
This is not a financial advice
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”
Cadena de bloques
Minado de transacciones
Minado de transacciones
Wallet
Minar
Mecanismos de consenso
Blockchains
Ethereum
Nosotros desplegaremos en la testnet Rinkeby
EVM
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;
}
}
Inspeccionando en etherscan
Demo
ERC-20
ERC, Ethereum Request for Comments.
Medio de pago.
Gobernanza de DAOs, decentralized autonomous organization.
ERC-721
Asset digital identificable de forma inequívoca
Coleccionable
Certificado de "propiedad"
Tarjeta de fidelidad, pertenencia a un club/comunidad
JSON-RPC API
❯ 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// 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
// 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
// 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
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
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};
}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;
}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