Visão Geral
Os dApps (aplicativos descentralizados) são parte integrante do ecossistema de desenvolvimento Ethereum. Existem milhares de dApps que já residem na blockchain Ethereum. Neste guia, aprenderemos como construir um dApp da Ethereum que armazenará versões curtas de URLs.
Sumário
1 . O que é um dApp
2 . Por que construir um encurtador de URL para a Ethereum
3 . Iniciando nosso nó Ethereum
5 . O back end
6 . Juntando tudo
7 . Conclusão
O que é um dApp
Os dApps ou aplicativos descentralizados são bastante similares aos aplicativos tradicionais. Eles também têm um frontend e um backend. A diferença é que os backends dos dApps são executados em uma rede peer-to-peer descentralizada, enquanto os aplicativos tradicionais têm seu backend rodando em servidores centralizados.
Os dApps da Ethereum consistem em frontends e contratos inteligentes. Podemos construir a interface frontend/usuário em qualquer linguagem que seja capaz de se comunicar com o backend. O front-end também pode ser descentralizado e hospedado em serviços de armazenamento como IPFS ou Swarm.
Os backends dos dApps, que são contratos inteligentes, são principalmente escritos em Solidity - uma linguagem orientada a objetos de alto nível, e depois implantados na rede blockchain Ethereum. O front-end pode então interagir com o contrato inteligente, executando partes do código do contrato inteligente subjacente.
Para saber mais sobre como redigir e implantar contratos inteligentes, verifique nosso Guia Solidity .
Por que construir um encurtador de URL para a Ethereum
Desde os primórdios da Internet, a quantidade de dados que ela manipulava vinha aumentando continuamente em níveis exponenciais. Todas estas informações - dados pessoais, dados financeiros, mídia, etc., residem e são tratadas por servidores centrais. As empresas que administram estes servidores centrais têm controle total sobre estes dados e podem usá-los de forma indevida, vendê-los ou negar totalmente o acesso a estes dados, que tecnicamente não são de sua propriedade. A abordagem do servidor centralizado não é resiliente à censura ou a outras formas de negligência. Um grande exemplo disto é Google fechando seu encurtador de URL. É aqui que os dApps têm uma vantagem significativa sobre os aplicativos tradicionais; os dApps ajudam a descentralizar as informações e a tornar a Internet mais segura e acessível a todos. Em nenhum momento, uma única empresa pode possuir seus dados. Os dados estão simplesmente espalhados por muitos locais (descentralizados).
Agora que está claro por que os dApps são tão úteis, é hora de construir um encurtador de URL alternativo na Ethereum:
Pré-requisitos
- NodeJS instalado em seu sistema
- Git instalado em seu sistema
- Um editor de texto
- Terminal denominado Linha de Comando
- Metamask e algum ETH para taxas de gas
Iniciando nosso nó Ethereum
Para o propósito deste guia, poderíamos usar qualquer cliente Ethereum, tais como o Geth ou OpenEthereum (anteriormente conhecido como Parity). Como o processo de lançamento desses clientes é um processo de bastante consumo de tempo e dedicação, nós vamos pegar um endpoint gratuito do QuickNode para torná-lo completamente indolor.
Inscreva-se no QuickNode, e inicie seu nó gratuito na rede principal ETH.
Depois de criar seu nó gratuito ethereum, copie e salve seu endpoint do Provedor HTTP, pois você precisará dele mais tarde:
Construindo nosso dApp
Certifique-se de que o NodeJS e o git estejam instalados em sua máquina.
Para verificar se o NodeJS 10+ está instalado em seu sistema, digite o seguinte em seu terminal/cmd:
$ node -v
Se não estiver instalado, você pode baixar a versão LTS do NodeJS a partir do website oficial.
Para verificar se o git está instalado em seu sistema, digite o seguinte em seu terminal/cmd:
$ git --version
Se não estiver instalado, você pode fazer o download do git a partir do website oficial
Faremos/construiremos um encurtador de URL chamado 0xsu, com os repositórios localizados aqui no GitHub
O contrato Solidity + explicação
Antes de construir nosso dApp, primeiro vamos entender o contrato inteligente sobre o qual vamos construir nosso dApp.
pragma solidity ^0.5.2;
contract URLShortner {
struct URLStruct {
address owner;
string url;
bool exists;
bool paid;
}
mapping (bytes => URLStruct) lookupTable;
mapping (address => bytes[]) public shortenedURLs;
address[] accts;
address payable owner;
event URLShortened(string url, bytes slug, address owner);
constructor() public { owner = msg.sender; }
function shortenURLWithSlug(string memory _url, bytes memory _short, bool paid) public payable {
bool paidDefault = false;
if (!lookupTable[_short].exists){
lookupTable[_short] = URLStruct(msg.sender, _url, true, paid||paidDefault);
shortenedURLs[msg.sender].push(_short);
if(shortenedURLs[msg.sender].length < 1) {
accts.push(msg.sender);
}
emit URLShortened(_url, _short, msg.sender);
}
}
function shortenURL(string memory url, bool paid) public payable {
bool paidDefault = false;
bytes memory shortHash = getShortSlug(url);
return shortenURLWithSlug(url, shortHash, paid||paidDefault);
}
function listAccts() public view returns (address[] memory){
return accts;
}
function getURL(bytes memory _short) public view returns (string memory) {
URLStruct storage result = lookupTable[_short];
if(result.exists){
return result.url;
}
return "FAIL";
}
function kill() public {
if (msg.sender == owner) selfdestruct(owner);
}
// particulares
function getShortSlug(string memory str) internal pure returns (bytes memory) {
bytes32 hash = sha256(abi.encodePacked(str));
uint main_shift = 15;
bytes32 mask = 0xffffff0000000000000000000000000000000000000000000000000000000000;
return abi.encodePacked(bytes3(hash<<(main_shift*6)&mask));
}
}
Explicação do código acima:
Linha 1: Declarando a versão do Solidity
Linhas 3-9: Declarando nosso contrato inteligente como URLShortner, em seguida, criando uma struct URLStruct para armazenar os detalhes relevantes de um URL abreviado; owner (proprietário) da classe address (endereço), que armazena o endereço da pessoa que encurtou o URL; url do tipo string, para armazenar o URL a ser encurtado; exists do tipo booleano, que armazenará se o URL estiver presente; paid (pago) do tipo booleano, para verificar se a transação para encurtar o URL é paga.
Linha 10: Criar um mapeamento para mapear a versão curta do URL para URLStruct.
Linha 11: Criar um mapeamento onde o endereço de um determinado proprietário (uma pessoa encurtando o URL) será mapeado para uma série de URLs encurtados que essa pessoa tenha encurtado. Assim, cada pessoa que encurtar URLs terá uma matriz de URLs encurtados mapeados para seu endereço.
Linha 12: Criação de uma array chamada accts do tipo address, que armazenará os endereços das contas que tenham interagido com o contrato inteligente encurtando o URL.
Linha 13: Declarando owner do tipo address payable (endereço pagável), que permitirá a esse proprietário acessar os primitivos úteis para administrar os ethers.
Linha 14: Criando um evento URLShortened que será emitido através do RPC em nós ethereum no mundo inteiro - qualquer frontend pode ouvir este evento.
Linha 16: Criando um construtor e especificando o endereço do criador do contrato inteligente - este endereço tem privilégios especiais.
Linhas 18-28:
- Criando uma função shortenURLWithSlug que permitirá a uma pessoa/indivíduo especificar a versão encurtada do URL, a função é de estado público, o que significa que pode ser acessada fora dos escopos desta função e por outros contratos.
- Definindo o payDefault como falso.
- Uma condição if para verificar se a versão curta do URL dado existe ou não e, se não existir, será adicionada à tabela de pesquisa usando um URLStruct.
- Adicionando o URL curto (slug - é a parte de um URL que pode ser legível tanto para humanos quanto para mecanismos de busca) à pessoa que encurtou o URL nos shortenedURLs.
- Uma condição para verificar se o número de URLs encurtados pela pessoa/indivíduo é inferior a um e se a pessoa/indivíduo está encurtando o URL pela primeira vez. Então ela será adicionada ao array de accts que vimos na linha 12.
- Emitindo o URL do evento URLShortened.
Linhas 30-34: Criando uma função shortenURL. Esta função é a mesma que a anterior, com exceção de que a pessoa/indivíduo não consegue especificar a versão curta. Eles colam o URL e ele será encurtado usando o método getShortSlug que veremos mais tarde.
Linhas 36-38: Criando uma função listAccts que facilitará um método getter para obter as contas da array accts, uma vez que a array accts não é pública.
Linhas 40-46: Criando uma função getURL, para verificar se o URL curto existe em lookupTable e se existe, ela retornará o URL curto. Se não existe, retornará a palavra FAIL (falha).
Linhas 48-50: Criando uma função kill e uma condição if na qual verificará se o proprietário (a pessoa da linha 14) está conectado ao contrato. Se sim, então ele irá parar a conexão/associação.
Linhas 53-58: Criando uma função getShortSlug que é um método privado pelo qual geramos o URL curto.
Para economizar tempo e ETH, já implantamos este contrato inteligente e seu endereço está embutido em nossas bibliotecas de ajuda e no código abaixo.
O front end
Comece criando um diretório para nosso dApp e depois clone os repositórios front-end necessários nele:
$ mkdir 0xsu
$ cd 0xsu
$ git clone [https://github.com/dU4E/0xsu-front-end/](https://github.com/dU4E/0xsu-front-end/)
$ cd 0xsu-front-end
O front-end do nosso aplicativo é feito usando o react. Então vamos instalar todas as dependências necessárias.
$ npm i react
$ npm install -g create-react-app
O frontend funcionará na porta número 3000, e a porta 3001 é onde o servidor express funcionará no próximo passo.
Agora vamos entender algumas partes importantes do arquivo principal de nosso aplicativo react do front end:
import React from "react";
import Du4e from "@sahilsen/0xsu-js-lib";
import "./App.css";
import Navbar from "react-bootstrap/Navbar";
import { Badge, Container, Form, Modal, Button, Col } from "react-bootstrap";
class App extends React.Component {
state = {
shortURL: "",
copied: null,
urls: [],
show: false
};
constructor(props) {
super(props);
this.shortenUrl = this.shortenUrl.bind(this);
this.copy = this.copy.bind(this);
this.handleClose = this.handleClose.bind(this);
this.viewAcct = this.viewAcct.bind(this);
this.shortener = new Du4e();
this.shortener.onTxSend = txid => {
console.log("TXID", txid);
this.setState({ shortURL: "Aguardando a confirmação da transação" });
};
this.shortener.onURLShortened = (err, result) => {
console.log("resultado", result);
this.setState({ shortURL: result.args.slug });
};
}
copy() {
let el = document.createElement("textarea");
el.value = this.state.longURL;
el.setAttribute("readonly", "");
el.style.position = "absolute";
el.style.left = "-9999px";
document.body.appendChild(el);
el.select();
document.execCommand("copy");
document.body.removeChild(el);
this.setState({ copied: true });
}
handleClose() {
this.setState({ show: false });
}
viewAcct() {
this.shortener.listOfUrls(list => {
console.log(list);
this.setState({ urls: list });
});
}
shortenUrl(evt) {
evt.preventDefault();
this.shortener.shortenUrl(this.refs.short.value, { cb: () => {} });
}
render() {
let { shortURL } = this.state;
return (
<div>
<Navbar bg="light">
<Navbar.Collapse className="justify-content-end">
<a href="#" onClick={this.viewAcct}>
Conta
</a>
</Navbar.Collapse>
</Navbar>
<Container className="text-center">
<br />
<h1 className="App">0xSU.co</h1>
<p>
Seu criador de URL curto Ethereum de vizinhança amigável
</p>
<br />
<form onSubmit={this.shortenUrl}>
<Form.Row>
<Col sm={9}>
<Form.Control
size="lg"
type="text"
ref="short"
placeholder="Cole seu URL longo aqui"
/>
</Col>
<Col sm={3}>
<Button variant="primary" size="lg" type="submit">
Encurtar
</Button>
</Col>
</Form.Row>
</form>
<div className={shortURL ? "" : "hidden"}>
<br />
<br />
<span className={shortURL.startsWith("Waiting") ? "loading" : ""}>
{shortURL.startsWith("Waiting")
? shortURL
<p id="gdcalert2" ><span style="color: red; font-weight: bold">>>>>> gd2md-html alert: Definition ↓↓ outside of definition list. Missing preceding term(s)? </span><br>(<a href="#">Back to top</a>)(<a href="#gdcalert3">Next alert</a>)<br><span style="color: red; font-weight: bold">>>>>> </span></p>
: `http://localhost:3001/${shortURL}`}
</span>
{!shortURL.startsWith("Waiting") && (
<Badge variant="success" onClick={this.copy} className="copy">
{this.state.copied ? "Copied!" : "Copy"}
</Badge>
)}
</div>
<br />
<br />
<br />
<br />
<a href="[https://github.com/du4e](https://github.com/du4e)">Aprenda mais / código fonte</a>
</Container>
<Modal
show={this.state.urls.length > 0 || this.state.show}
onHide={this.handleClose}
>
<Modal.Header closeButton>
<Modal.Title>Seus URLs encurtados</Modal.Title>
</Modal.Header>
<Modal.Body>
{this.state.urls.map(x => {
return (
<p key={x}>
<a href={`http://localhost:3001/${x}`}>{x}</a>
</p>
);
})}
</Modal.Body>
</Modal>
</div>
);
}
}
export default App;
Linhas 1-5: Importando as bibliotecas e ativos necessários.
Linha 7: Estabelecendo um componente chamado App
Linhas 8-13: Inicializando o estado que ajudará na obtenção de dados remotos (do contrato).
Linhas 15-19: Estabelecendo vinculação de métodos a serem utilizados - para que utilizem o "this" correto.
Linha 20: Configurando o objeto Du4e da biblioteca Du4e, que é uma biblioteca para encapsulamento de URLs.
Linhas 21-28: Anexando dois manipuladores de eventos, onTxSend, que irá escutar a transação, e onURLShortened que irá escutar o URL do eventoShortened do contrato e quando o evento URLShortened for emitido, mudará o estado e disponibilizará o shortURL para o componente do App react.
Linhas 30-41: copy() estabelece um elemento temporário na página que permite que o texto do URL encurtado seja copiado.
Linhas 43-45: handleClose() fecha o Modal, que é uma caixa de mensagens com funcionalidades.
Linhas 47-52: viewAcct() abre um Modal que exibirá a lista de URLs abreviados pelo usuário atual, definindo o estado dos URLs para esse usuário.
Linha 54-57: shortenUrl encurta o URL usando a biblioteca Du4e.
Linha 59-143: um monte de código JSX que anexa os eventos e renderiza o aplicativo.
O back end
Agora, precisamos configurar nosso servidor e o contrato inteligente (que vimos anteriormente). Precisamos voltar ao nosso diretório-pai /0xsu e clonar o repositório do contrato inteligente e o repositório do servidor Express dentro dele.
Nota: estamos usando o servidor Express, mas você pode escolher outras opções, tais como Sinatra e Flask daqui
$ git clone [https://github.com/dU4E/0xsu-smart-contracts](https://github.com/dU4E/0xsu-smart-contracts)
Nota: O contrato inteligente para este aplicativo já está implantado na blockchain Ethereum, e podemos consultar as funções do contrato usando sua ABI e o endereço do contrato.
Agora, volte ao diretório-pai 0xsu a partir de uma nova janela de terminal/cmd e clone o repositório do servidor Express.
$ git clone [https://github.com/dU4E/0xsu-express-server](https://github.com/dU4E/0xsu-express-server)
$ cd 0xsu-express-server
Também precisaremos instalar o Express; digite o seguinte em seu terminal/cmd:
$ npm i express
Abra o arquivo .env em seu editor de texto e, na segunda linha, substitua ADD_YOUR_QUICKNODE_URL pelo URL QuickNode HTTPS que você salvou anteriormente.
Vamos entender o que realmente nosso servidor Express faz ao ver o arquivo index.js a partir do repositório 0xsu-express-server.
const fs = require('fs')
const bodyParser = require('body-parser')
const express = require('express')
const Web3 = require('web3')
const path = require('path')
const dotenv = require('dotenv').config({ path: path.resolve('.env')})
const app = express()
const port = 3001
const abi = JSON.parse(fs.readFileSync(process.env.ABI_PATH))
const contractAddr = process.env.CONTRACT_ADDRESS
const web3 = new Web3(Web3.givenProvider || process.env.PROVIDER_URL)
app.set('view engine', 'ejs')
app.get('/:short', async function (req, res) {
const contract = new web3.eth.Contract(abi, contractAddr)
let slug = req.params.short
// pegue o url e se ele foi pago ou não
let destination = await contract.methods.getURL(slug).call()
res.redirect(destination !== '"FAIL' ? destination : "/")
})
app.listen(port, () => console.log(`Aplicativo de exemplo ouvindo na porta ${port}!`))
Explicação do código acima:
Linhas 1-6: Importando os pacotes/bibliotecas necessários.
Linhas 7-11: Criando um novo aplicativo express, declarando o número da porta na qual nosso aplicativo express será executado, declarando uma variável abi que irá armazenar a ABI do contrato inteligente obtida do arquivo .env, declarando uma variável contractAddr que irá armazenar o endereço do contrato inteligente obtido do arquivo .env, uma variável web3 que irá se conectar ao endpoint do QuickNode que colamos no arquivo .env.
Linha 13: Definindo o EJS como o mecanismo de visualização de nosso aplicativo express.
Linhas 15-21: Obtendo a versão curta do URL, configurando a web3 usando abi e contractAddr, buscando o URL curto e armazenando na variável slug, obtendo informações sobre o URL do contrato inteligente e se o URL curto fornecido existir, ele será redirecionado para o URL real.
Linha 23: Imprimindo uma mensagem no console, que será impressa após a execução bem sucedida do código index.js do aplicativo express.
Agora você deve ter todas as partes necessárias do código instaladas e duas janelas de terminal abertas, uma com o diretório 0xsu-front-end e a outra com o 0xsu-express-server.
Juntando tudo
- Executando o Servidor
Mude para a janela com o diretório /express-server aberto e inicie o servidor Express digitando o seguinte comando:
$ node index
A saída ficará assim:
Agora, mude para a outra janela de terminal e inicie o aplicativo react digitando o seguinte em seu terminal/cmd:
$ npm start
Se tudo correr bem, você deve ser capaz de abrir o aplicativo em seu navegador em http://localhost:3000/ e desde que você tenha o plugin do navegador Metamask, uma janela se abrirá para pedir que você conceda permissão para o aplicativo.
- Testando seu dApp
Coloque qualquer URL que você deseja encurtar no campo de texto e clique em Shorten (Encurtar). Isso abrirá a Metamask para confirmar a transação que exigirá uma taxa de gas. Como estamos interagindo com o contrato inteligente implantado na rede da blockchain Ethereum, precisamos pagar uma taxa para isso. Você pode saber mais sobre transações e taxas de gas neste guia - [Como reenviar uma transação com preço de gas mais alto usando ethers-js]
Após a conclusão da transação, você verá uma mensagem com o URL encurtado abaixo do campo de texto:
Você também pode ir para a seção Account (Conta) na parte superior direita e clicar no link para abrir o link real em https://localhost:3001/shortenedURL.
O URL encurtado gerado permanecerá na rede Ethereum e poderá ser acessado a qualquer momento.
Conclusão
Parabéns pela construção de um verdadeiro aplicativo descentralizado! Com uma pequena ajuda do QuickNode esperamos que o processo tenha sido rápido e sem dor.
Inscreva-se em nossa newsletter para mais artigos e guias sobre a Ethereum. Se você tiver algum feedback, sinta-se à vontade para entrar em contato pelo Twitter. Você pode sempre conversar conosco em nossa comunidade do Discord, com alguns dos desenvolvedores mais legais que você já conheceu :)
23 de setembro de 2022
Esse artigo está em QuickNode e foi traduzido por Fátima Lima. O original pode ser lido aqui.
Oldest comments (0)