Шаг за шагом: ReactJS dApp с эмулятором OnFlow

Этот материал был создан благодаря @vitiv (Явен Витив)!
__
В отличие от традиционных приложений, у которых внутренняя часть работает на централизованных серверах, код внутренней части dApp работает автономно в децентрализованной сети, такой как сеть OnFlow.

Смарт-контракты — это программы, используемые для выполнения соглашений, запускаемых при выполнении заранее определенных условий. Они позволяют участвующим сторонам безопасно высвобождать средства, регистрировать транспортные средства, выписывать билеты и даже отправлять уведомления без посредников.

Смарт-контракты обеспечивают высокий уровень безопасности и прозрачности, скорость, точность и заметную экономическую эффективность. Они могут использоваться в различных отраслях, включая здравоохранение, дистрибуцию, розничную торговлю и многое другое.
dApps позволяют взаимодействовать со смарт-контрактами, развернутыми на блокчейне.

Как создать приложение на стороне клиента, взаимодействующее со смарт-контрактом в сети блокчейн

Приложение, которое мы собираемся создать, будет обладать следующей функциональностью:

  • Кнопки входа и выхода из системы
  • Профили пользователей
  • Возможность обновлять профили пользователей
  • Адрес счета, напрямую связанный с сетью блокчейн

Шаг 1. Установка зависимостей

  1. Установите менеджер пакетов Node (NPM)
  2. Установите Flow CLI для запуска локального эмулятора
  3. Клонируйте репозиторий fcl-dev-wallet в отдельную папку для запуска dev wallet и установки npm.

Шаг 2. Установка ReactJS dApp и пакетов npm

Создайте ReactJS dApp:

Установите пакеты OnFlow из каталога my-first-dapp:

npm i @onflow/fcl @onflow/types

Шаг 3. Настройка Tailwind CSS

Этот шаг необязателен, но если хотите, вы можете прочитать, как настроить Tailwind CSS.

Шаг 4. Запуск локального эмулятора с dev-кошельком

В репозитории fcl-dev-wallet вам нужно будет выполнить эти две команды:

Шаг 5. Создание компонента AppJS со встроенными кнопками входа и выхода из системы

В файле src/AppJS создайте компонент AppJS с логикой входа и выхода:

mport * as fcl from "@onflow/fcl";

import './App.css';
import { useEffect, useState } from "react";

fcl
    .config()
    // Point App at Emulator REST API
    .put("accessNode.api", "http://localhost:8888")
    // Point FCL at dev-wallet (default port)
    .put("discovery.wallet", "http://localhost:8701/fcl/authn")


function App() {

    const [isLogged, setIsLogged] = useState(false);
    const [user, setUser] = useState();

    useEffect(() => {
        fcl.currentUser.subscribe((account) => {
            setUser(account);
            setIsLogged(account.addr)
        });
    }, [])

    const logIn = () => {
        fcl.authenticate();
    }

    const logOut = () => {
        fcl.unauthenticate();
    }

    return (
        <div className="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
            {isLogged ? (
                <div className="p-3">
                    <h3 className="text-2xl">{user.addr}</h3>
                    <button onClick={logOut}
                            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                        Logout
                    </button>
                </div>
            ) : (
                <button onClick={logIn}
                        className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                    Login
                </button>
            )}
        </div>
    );
}

export default App;
Вход в полноэкранный режим Выход из полноэкранного режима

Шаг 6. Запуск ReactJS dApp

Выполните команду npm-start и откройте страницу в http://localhost:3000/.

Шаг 7. Создание первого смарт-контракта

Создайте файл Profile.cdc в каталоге fcl-dev-wallet/cadence/contract:

Добавьте смарт-контракт в конфигурационный файл каталога fcl-dev-wallet/flow.json:

{
  …
  "contracts": {
    "Profile": {
      "source": "./cadence/contracts/Profile.cdc"
      "aliases": {
        "emulator": "0xee82856bf20e2aa6"
     }
    },
    …
  },
  …
  "deployments": {
    "emulator": {
      "emulator-account": ["FUSD", "Profile"]
    }
  }
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Разверните файл Profile.cdc на локальном эмуляторе:

flow project deploy --update

Шаг 8. Создание первого скрипта cadence

Извлеките данные из сети блокчейна:

const getProfile = async () => {
        const [firstName, lastName, email] = await fcl.send([
            fcl.script`
            import Profile from 0xf8d6e0586b0a20c7

            pub fun main(): [String] {
              return [Profile.firstName, Profile.lastName, Profile.email]
            }
            `
        ]).then(fcl.decode)

        setProfile({ firstName, lastName, email })
    }
view raw
Войдите в полноэкранный режим Выход из полноэкранного режима

Шаг 9. Создание первой транзакции блокчейна

Создайте транзакцию блокчейн для изменения данных в сети блокчейн:

const updateProfile = async () => {
        const txId = await fcl.send(
            [
                fcl.transaction`
                import Profile from 0xProfile

                transaction(newFirstName: String, newLastName: String, newEmail: String) {

                  prepare(signer: AuthAccount) {

                  }

                  execute {
                    Profile.updateProfile(newFirstName: newFirstName, newLastName: newLastName, newEmail: newEmail)
                  }
                }
                `,// update profile transaction
                fcl.args([
                    fcl.arg(profile.firstName, t.String),
                    fcl.arg(profile.lastName, t.String),
                    fcl.arg(profile.email, t.String)
                ]), // parameters of the transaction
                fcl.proposer(fcl.authz), // The account that initiate this transaction
                fcl.payer(fcl.authz), // The account that pays the fee for this transaction (free on local emulator)
                fcl.authorizations([fcl.authz]), // A list of the accounts that are authorizing this transaction to mutate to their on-chain account state.
                fcl.limit(9999), // The maximum number of computational units that can be used to execute this transaction.
            ]
        ).then(fcl.decode);

        alert(`The transaction executed successfully`);
    }
Войти в полноэкранный режим Выйти из полноэкранного режима

Шаг 10. Обновление контента

Добавьте скрипт cadence и транзакцию blockchain в компонент AppJS и создайте от для обновления контента (профиля пользователя):

import * as fcl from "@onflow/fcl";
import * as t from "@onflow/types"

import './App.css';
import { useEffect, useState } from "react";

fcl
    .config()
    // Point App at Emulator REST API
    .put("accessNode.api", "http://localhost:8888")
    // Point FCL at dev-wallet (default port)
    .put("discovery.wallet", "http://localhost:8701/fcl/authn")
    .put("0xProfile", "0xf8d6e0586b0a20c7");

function App() {

    const [isLogged, setIsLogged] = useState(false);
    const [user, setUser] = useState();
    const [profile, setProfile] = useState();

    useEffect(() => {
        fcl.currentUser.subscribe((account) => {
            setUser(account);
            setIsLogged(account.addr)
            getProfile();
        });
    }, [])

    const getProfile = async () => {
        const [firstName, lastName, email] = await fcl.send([
            fcl.script`
            import Profile from 0xProfile

            pub fun main(): [String] {
              return [Profile.firstName, Profile.lastName, Profile.email]
            }
            `
        ]).then(fcl.decode)

        setProfile({ firstName, lastName, email })
    }

    const updateProfile = async (e) => {
        e.preventDefault();

        const txId = await fcl.send(
            [
                fcl.transaction`
                import Profile from 0xProfile

                transaction(newFirstName: String, newLastName: String, newEmail: String) {

                  prepare(signer: AuthAccount) {

                  }

                  execute {
                    Profile.updateProfile(newFirstName: newFirstName, newLastName: newLastName, newEmail: newEmail)
                  }
                }
                `,// update profile transaction
                fcl.args([
                    fcl.arg(profile.firstName, t.String),
                    fcl.arg(profile.lastName, t.String),
                    fcl.arg(profile.email, t.String)
                ]), // parameters of the transaction
                fcl.proposer(fcl.authz), // The account that initiate this transaction
                fcl.payer(fcl.authz), // The account that pays the fee for this transaction (free on local emulator)
                fcl.authorizations([fcl.authz]), // A list of the accounts that are authorizing this transaction to mutate to their on-chain account state.
                fcl.limit(9999), // The maximum number of computational units that can be used to execute this transaction.
            ]
        ).then(fcl.decode);

        console.log(txId);
        alert(`The transaction executed successfully`);
    }

    const logIn = () => {
        fcl.authenticate();
    }

    const logOut = () => {
        fcl.unauthenticate();
    }

    const handleInputChange = (e) => {
        setProfile({ ...profile, [e.target.id]: e.target.value });
    }


    const profileBlock = profile ? (<div className="border-b-2 block md:flex">
        <div className="w-full p-8 bg-white shadow-md">
            <form onSubmit={updateProfile} autoComplete="off">
                <div className="pb-6">
                    <label htmlFor="firstName"
                          className="font-semibold text-gray-700 block pb-1">First Name</label>
                    <div className="flex">
                        <input id="firstName" required className="border-1 bg-gray-200 rounded-r px-4 py-2 w-full"
                              type="text" value={profile.firstName} onChange={handleInputChange}/>
                    </div>
                </div>
                <div className="pb-6">
                    <label htmlFor="lastName"
                          className="font-semibold text-gray-700 block pb-1">Last Name</label>
                    <div className="flex">
                        <input id="lastName" required className="border-1 bg-gray-200 rounded-r px-4 py-2 w-full"
                              type="text" value={profile.lastName} onChange={handleInputChange}/>
                    </div>
                </div>
                <div className="pb-6">
                    <label htmlFor="email"
                          className="font-semibold text-gray-700 block pb-1">Email</label>
                    <div className="flex">
                        <input id="email" autocomplete="nope" required
                              className="border-1 bg-gray-200 rounded-r px-4 py-2 w-full"
                              type="email" value={profile.email} onChange={handleInputChange}/>
                    </div>
                </div>
                <div>

                    <button type="submit"
                            className="bg-green-500 hover:bg-green-700 text-white font-bold py-2 px-4 rounded">
                        Update Profile
                    </button>
                </div>
            </form>
        </div>
    </div>) : '';

    return (
        <div className="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
            {isLogged ? (
                <div className="p-3">
                    <span className="inline-block text-2xl mr-2">Account address: {user.addr}</span>
                    <button onClick={logOut}
                            className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                        Logout
                    </button>
                    {profileBlock}
                </div>
            ) : (
                <button onClick={logIn}
                        className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
                    Login
                </button>
            )}
        </div>
    );
}

export default App;
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь вы можете протестировать свое первое децентрализованное приложение.
__
В настоящее время около 4 000 децентрализованных приложений используются в правительстве, здравоохранении и даже в азартных играх. И положительная динамика, несомненно, свидетельствует о целесообразности и прибыльности внедрения решений на основе блокчейна. Если говорить о доминировании на рынке, то децентрализованные приложения вряд ли заменят традиционные приложения. Однако ожидается, что они получат больший импульс в бизнес-среде.

Вот некоторые статистические данные, которые следует рассмотреть:

  • За один день совершается более 199000 биткоин-транзакций
  • 10 процентов мирового населения владеют криптовалютами
  • 16 процентов американцев инвестировали в криптовалюту
  • Глобальный рынок блокчейна, как ожидается, достигнет $34 млрд к 2026 годуВ dApps личные и деловые данные хранятся в отдельных неизменяемых блоках в среде блокчейна. Это означает, что конфиденциальные данные защищены от эксплуатации — одной из самых серьезных проблем, с которой могут столкнуться как молодые стартапы, так и зрелые международные корпорации.

Оцените статью
devanswers.ru
Добавить комментарий