Web3 – Jak vypadá vývoj decentralizované aplikace

18. 4. 2023 Azure, Programming

Možná jste slyšeli o fenoménu decentralizovaných aplikací, NFT anebo jiných formách využití blockchain. Zajímalo mě, jak se taková Web3 aplikace tvoří, a proto jsem se pustil do projektu s cílem vytvořit malou demo Web3 aplikaci.

Co je to vlastně Web3?

Web3 je vize nové generace internetu, která si zakládá na větší decentralizaci a ochraně osobních dat s využitím blockchain. Zatím neexistuje žádná jednotná definice, jak by měl Web3 vypadat, a tak se pohled na něj a míra decentralizace může velmi lišit. Někdo by mohl například říct, že ve Web3 nejsou potřeba žádné cloudové služby a vše poběží na sdílené peer-to-peer síti. Dnes je ale většina Web3 aplikací hostována na cloudu a využívá blockchain pouze pro tzv. Smart Contract.

Smart Contract

Jednoduše řečeno, jedná se o program běžící na blockchain. Jeho důležitou vlastností je, že jakmile jej nahrajeme na síť, není možné ho smazat a interakce s ním jsou nevratné. Blockchain si také můžeme představit jako takovou smlouvu, na niž nepotřebujeme právníka, jelikož na její dodržení dohlíží algoritmus a decentralizovaná síť, jež zaručuje, že smluvní podmínky nemůžou být změněny.

Jak si napsat vlastní Smart Contract

Smart Contract se píše ve speciálním jazyce Solidity. Pro vytvoření bezpečného Smart Contract, můžete použít knihovnu OpenZeppelin, která za vás implementuje základní funkce standardu ERC-20 (nebo i jiných standardů). Dokonce existuje i klikací prostředí, které template kódu vytvoří úplně za vás.

OpenZeppelin Wizard pro Web3
Ukázka prostředí OpenZeppelin Wizard | Zdroj: OpenZeppelin

Následně si jej můžete rovnou i importovat do Remix IDE. To je webové vývojové prostředí, kde můžeme vytvářet Smart Contract a které obsahuje mimo jiné i prostor pro testovaní na virtuální síti.

Remix IDE prostředí pro Web3 programování
Ukázka prostředí Remix IDE | Zdroj: Remix IDE

Testování

Abyste nemuseli utrácet za transakční poplatky za nahrání na mainnet (= hlavní Ethereum síť), můžete využít různé testovací sítě nebo si simulovat vlastní lokální blockchain. Jeden ze způsobů, jak otestovat Smart Contract, je využít Remix IDE. Zde lze Smart Contracty nahrát na testovací VM (Virtual Machine), na kterém běží virtuální blockchain. Navíc přímo v prostřední můžete funkce volat a nemusíme si tak vytvářet vlastní aplikaci.

Remix IDE nahrání na VM
Nahrání na VM | Zdroj: Remix IDE
Remix IDE volání Smart Contract funkcí
Volání funkcí uvnitř Remix IDE | Zdroj: Remix IDE

Propojení Web3 aplikace s front-endem

Nyní si už ale ukážeme, jak propojit Smart Contract s webovou aplikací. Pro ukázku si vytvoříte jednoduchý Smart Contract, který bude pouze inkrementovat číslo uložené na blockchain.

Co budete potřebovat?

Inicializace projektu a nahrání na lokální blockchain

Základní soubory si inicializujete příkazy npm init -ytruffle init. Teď nainstalujte Web3.JS pomocí npm install web3, a pokud budete používat OpenZeppelin, nainstalujete ho pomocí npm install @openzeppelin/contracts. Následně si ve složce „Contracts“ vytvořte nový soubor s příponou .sol, ve kterém bude váš Smart Contract.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

contract Demo {
    uint256 public number;

    function incrementNumber() public {
        number += 1;
    }
}

Ve složce „Migrations“ si ještě vytvoříme soubor „2_deploy_<Název souboru>.js“, kterým nastavíme .sol soubor pro kompilaci a následné nahrání na blockchain. Místo „Demo“ doplňte název vašeho .sol souboru.

const Demo = artifacts.require("Demo");
module.exports = function (deployer) {
      deployer.deploy(Demo);
};

Teď si spusťte Ganache a vytvořte nový workspace. Do něj pomocí „ADD PROJECT“ vložte „truffle_config.js“ a klikněte na „SAVE WORKSPACE“.

Tvorba workspace v Ganache
Vytvoření nového workspace v Ganache | Zdroj: Vlastní

Nakonec už jenom nakonfigurujte truffle_config.js. Hodnoty nastavte podle toho, co máte v ganache (například port může být 8545).

module.exports = {
    networks: {
       development: {
            host: "127.0.0.1",
            port: "7545",
            network_id: "*",
        },
    },

    compilers: {
      solc: {
        version: "^0.8.4"
      }
    },
};

A konečně spusťte příkaz truffle deploy, čímž se vám Smart Contract zkompiluje a nahraje na lokální blockchain.

Přidání sítě do Metamask

Teď, když vám běží na lokální síti virtuální blockchain, se na něj můžete připojit přes Metamask. To uděláte tak, že kliknete na výběr sítí a vyberete „Add Network“.

Výběr sítě v Metamask
Výběr sítí v Metamask | Zdroj: Vlastní

Název můžete dát jakýkoliv chcete, RPC URL zkopírujte z Ganache, Chain ID by mělo být 1337, případně vám ho Metamask sám řekne. Jako symbol můžete zadat ETH nebo cokoliv jiného.

Vytvoření nové sítě v Metamask
Přidání sítě do Metamask | Zdroj: Vlastní

Jakmile máte síť přidanou, importujte si jednu z peněženek v Ganache do Metamask. Stačí v Ganache kliknout na ikonku klíče, zkopírovat privátní klíč a v Metamask importovat nový účet.

Zobrazení private key v Ganache
Zobrazení privátního klíče v Ganache | Zdroj: Vlastní
Importování účtu do Metamask
Importování účtu do Metamask | Zdroj: Vlastní

Pokud máte vybranou síť, kterou jste si přidali, tak byste v Metamask měli vidět 100 ETH u vašeho importovaného účtu.

Vytvoření stránky

Když máte Smart Contract nahraný na lokálním blockchain a Metamask je nastavený, zbývá vám už jen vytvořit stránku, přes kterou můžete se Smart Contract funkcemi interagovat. Na to nám slouží knihovna Web3.JS, kterou nainstalujete pomocí npm install web3. Pokud použijete můj následující kód, budete potřebovat ještě jQuery, kterou nainstalujete pomocí npm install jquery. Vytvořte si soubor „index.html“

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web3 e-shop demo</title>
</head>
<body>
    <p id="number"></p>
    <input type="button" id="increment" value="+1"></body>

    <script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script>
    <script type="text/javascript" src="node_modules/web3/dist/web3.min.js"></script>
    <script type="text/javascript" src="src/utils.js"></script>
    <script type="text/javascript" src ="src/index.js"></script>
</body>
</html>

a složku src ve ketré si vytvoříte soubory „index.js“

const update = async (contract) => {
	$("#number").html(`<b>${await contract.methods.number().call()}</b>`)
};

const increment = (contract, accounts) => {
    $("#increment").on("click", async (e) => {
        console.log("increment clicked");
        e.preventDefault();
        await contract.methods.incrementNumber().send({from: accounts[0]});
        update(contract);
    })
}

async function demo() {
    const web3 = await getWeb3();
    const accounts = await web3.eth.getAccounts();
    const contract = await getContract(web3);

    update(contract);
    increment(contract, accounts);
}

demo();

a „utils.js“.

const getWeb3 = () => {
    return new Promise((resolve, reject) => {
        window.addEventListener("load", async () => {
            if (window.ethereum) {
                const web3 = new Web3(window.ethereum);
                try {
                    await window.ethereum.request({ method: "eth_requestAccounts" });
                    resolve(web3);
                } catch (error) {
                    reject(error);
                }
            } else {
                reject("Must install MetaMask");
            }
        });
    });
};

const getContract = async (web3) => {
    const data = await $.getJSON("./build/contracts/Demo.json");

    const netId = await web3.eth.net.getId();
    const deployedNetwork = data.networks[netId];
    const contract = new web3.eth.Contract(
        data.abi,
        deployedNetwork && deployedNetwork.address
    );
    return contract;
};

Teď si ještě vytvořte soubor „server.js“, přes který budete stránku spouštět. K tomu budete navíc potřebovat express.js, který nainstalujete pomocí npm install express.

const express = require("express");

const app = express();
const port = 3000;

app.use(express.static(__dirname));


app.get('/', (req, res) => {
    res.render('index.html');
});

app.listen(process.env.PORT || port, () => console.log(`Listening on port ${port}`));

Stránku spustíte přes příkaz node server.js a v prohlížeči ji otevřete na http://localhost:3000. Nebudu popisovat, co kód dělá řádek po řádku. Nejdůležitějšími funkcemi jsou window.ethereum.request(), která slouží pro propojení peněženky s webovou aplikací, a dále contract.methods.<název funkce>.send()contract.methods.<název proměnné>.call(), kde send se používá pro volání funkcí a call pro čtení hodnot ze Smart Contractu.

Jak dostat Web3 aplikaci veřejně na internet

Pokud chcete, aby byl váš Smart Contract dostupný i mimo vaši lokální síť, musíte ho nahrát na některou z veřejných sítí. Nemusí to být přímo mainnet, kde bychom museli platit Ethereum za transakční poplatky. Existují také veřejné testovací sítě, nebo-li testnet, které nabízí tzv. faucety, z nichž si můžete zdarma vybrat testovací Ethereum. Já jsem použil testovací síť Kovan, která má faucet od Chainlinku.

Ethereum API

Abyste mohli na veřejnou síť Smart Contract nahrát, je nezbytné truffle propojit s API na Ethereum síť. Na to se nejčastěji používá Infura. Po založení účtu si vytvořte Ethereum projekt

Vytvoření projektu v Infura | Zdroj: Infura

a v sekci „Keys“ vyberte jako endpoint síť Kovan.

Výběr sítě v Infura | Zdroj: Infura

Teď si nastavte truffle_config.js na kovan. Musíte si ještě nainstalovat hdwallet-provider pomocí příkazu npm install @truffle/hdwallet-provider.

const HDWalletProvider = require("@truffle/hdwallet-provider");

module.exports = {
    networks: {
        kovan: {
            networkCheckTimeout: 10000,
            provider: () => {
                return new HDWalletProvider(
                <private key>,
                "wss://kovan.infura.io/ws/v3/<project ID>"
            )},
            network_id: "42",
        },
    },

Při deployování potřebujete i privátní klíč nebo můžete použít mnemonics které vám vygeneroval Metamask. Privátní klíč vám vygeneruje například ethcoldwallet. Po tom, co si ho importujete do Metamask, ho ale samozřejmě musíte dobít testnet ethery z faucetu. Project ID pouze zkopírujete z Infury. Teď už jen napíšete do konzole truffle migrate -–network kovan a váš Smart Contract by se měl úspěšně nahrát na veřejnou síť.

Nahrání Web3 aplikace na Azure

Nakonec zbývá nahrát stránku na web. K tomu můžete využít Azure Web App Service. V Azure portalu si vytvořte nový resource a vyberte Web App.

Výběr resource v Azure
Výběr resource v Azure | Zdroj: Azure

Jediné, co musíte nastavit, je „Runtime stack“ na „Node 16 LTS“ a můžete přejít rovnou na Review + create.

Vytvoření resource v Azure
Vytvoření resource | Zdroj: Azure

Jakmile bude vytvořený, přejděte na samotný resource. V kategorii Deployment vyberte Deployment center.

Deployment sekce
Deployment Center ve výběru sekce Deployment | Zdroj: Azure

Jako source vyberte GitHub. Pokud ještě aplikaci nemáte nahranou na GitHubu, vytvořte pro ni repo a potom ho v Azure vyberte.

Výběr repository v Deployment Center
Výběr repositáře v Deplayment Center | Zdroj: Azure

Jako poslední klikněte na tlačítko Save. Na GitHubu se automaticky vytvoří action, který se postará o deploy. Pokud chcete, můžete na GitHubu sledovat průběh. Jakmile se aplikace nahraje, přejděte na odkaz, který nám Azure vytvořil, a můžete stránku prohlížet.

Závěr

A jsme na konci. V tomto článku jste se naučili, jak si vytvořit jednoduchou Web3 aplikaci a nahrát ji veřejně na internet. Ne výrazně složitější Web3 aplikaci, kterou jsem vytvořil já, si můžete vyzkoušet zde nebo si prohlédnout její kód na GitHubu.