В этом руководстве вы узнаете, как создать dapp на блокчейне Aptos. Обычно dapp состоит из пользовательского интерфейса, написанного на JavaScript, который взаимодействует с одним или несколькими Move-модулями.
В этом руководстве мы будем использовать Move-модуль HelloBlockchain
, описанный в разделе Ваш первый модуль Move, и сосредоточимся на создании пользовательского интерфейса.
Мы будем использовать:
- Aptos SDK.
- Aptos Wallet, и
- Aptos CLI для взаимодействия с блокчейном.
Конечным результатом будет dapp, позволяющий пользователям публиковать и делиться фрагментами текста на блокчейне Aptos.
Полный исходный код этого руководства доступен здесь.
Необходимые условия
Aptos Wallet
Перед началом этого урока необходимо установить расширение Aptos Wallet.
После того как вы его установите:
- Откройте Кошелек и нажмите Создать новый кошелек. Затем нажмите Создать аккаунт, чтобы создать аккаунт Aptos.
- Скопируйте приватный ключ. Он понадобится вам для настройки Aptos CLI в следующем разделе.
ПРИМЕЧАНИЕ
Убедитесь, что на вашем счете достаточно средств для проведения транзакций, нажав кнопку Faucet.
Aptos CLI
- Установите Aptos CLI.
- Запустите
aptos init
, и когда он запросит ваш закрытый ключ, вставьте закрытый ключ из Aptos Wallet, который вы скопировали ранее. Это инициализирует Aptos CLI для использования той же учетной записи, которая используется в Aptos Wallet. - Запустите
aptos account list
, чтобы убедиться, что все работает.
Шаг 1: Создайте одностраничное приложение
Теперь мы настроим внешний пользовательский интерфейс для нашего dapp. В этом руководстве мы будем использовать [create-react-app](https://aptos.dev/tutorials/your-first-dapp#:~:text=We%20will%20use-,create%2Dreact%2Dapp,-to%20set%20up)
для настройки приложения, но ни React, ни create-react-app
не являются обязательными. Вы можете использовать ваш любимый JavaScript фреймворк.
$ npx create-react-app first-dapp --template typescript
$ cd first-dapp
$ npm start
Теперь у вас есть базовое приложение React, запущенное в браузере.
Шаг 2: Интеграция API Web3 Aptos Wallet
Aptos Wallet предоставляет Web3 API для dapps в window.aptos
. Вы можете увидеть, как он работает, открыв консоль браузера и выполнив await window.aptos.account()
. Он выведет адрес, соответствующий учетной записи, которую вы создали в Aptos Wallet.
Далее мы обновим наше приложение, чтобы использовать этот API для отображения адреса учетной записи кошелька.
Подождите, пока window.aptos
будет определен
Первым шагом при интеграции с API window.aptos
является задержка отображения приложения до наступления события window.onload
.
Откройте файл src/index.tsx
и измените следующий фрагмент кода:
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
в этом:
window.addEventListener('load', () => {
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
});
Это изменение гарантирует, что API window.aptos
будет инициализирован к моменту отображения приложения (если мы выполним отображение слишком рано, расширение Wallet, возможно, еще не успеет инициализировать API и, таким образом, window.aptos
будет undefined
).
(Необязательно) Настройка TypeScript для window.aptos
Если вы используете TypeScript, вы также можете сообщить компилятору о существовании API window.aptos
. Добавьте следующее в src/index.tsx
:
declare global {
interface Window { aptos: any; }
}
Это позволяет нам использовать API window.aptos
без необходимости делать (window as any).aptos
.
Отображение window.aptos.account()
в приложении
Теперь наше приложение готово к использованию API window.aptos.
Мы изменим src/App.tsx
, чтобы получить значение window.aptos.account()
( учетная запись кошелька) при начальном отображении, сохранить его в состоянии, а затем отобразить:
import React from 'react';
import './App.css';
function App() {
// Retrieve aptos.account on initial render and store it.
const [address, setAddress] = React.useState<string | null>(null);
React.useEffect(() => {
window.aptos.account().then((data : {address: string}) => setAddress(data.address));
}, []);
return (
<div className="App">
<p><code>{ address }</code></p>
</div>
);
}
export default App;
Обновите страницу, и вы увидите адрес своей учетной записи.
Добавим некоторые CSS
Затем замените содержимое src/App.css
:
a, input, textarea {
display: block;
}
textarea {
border: 0;
min-height: 50vh;
outline: 0;
padding: 0;
width: 100%;
}
Шаг 3: Используйте SDK для получения данных из блокчейна
Кошелек теперь интегрирован в наш dapp. Далее мы интегрируем Aptos SDK для получения данных из блокчейна. Мы будем использовать Aptos SDK для получения информации о нашей учетной записи и отображения этой информации на странице.
Добавьте aptos
в package.json
Сначала добавьте SDK в проект:
$ npm install --save aptos
Теперь вы увидите "aptos": "^0.0.20"
(или подобное) в вашем package.json
.
Создайте AptosClient
Теперь мы можем импортировать SDK и создать AptosClient
для взаимодействия с блокчейном (технически он взаимодействует с REST API, который взаимодействует с блокчейном).
Поскольку наша учетная запись кошелька находится в devnet, мы настроим AptosClient
для взаимодействия с devnet. Добавьте следующее в src/App.tsx
:
import { Types, AptosClient } from 'aptos';
// Create an AptosClient to interact with devnet.
const client = new AptosClient('https://fullnode.devnet.aptoslabs.com/v1');
function App() {
// ...
// Use the AptosClient to retrieve details about the account.
const [account, setAccount] = React.useState<Types.AccountData | null>(null);
React.useEffect(() => {
if (!address) return;
client.getAccount(address).then(setAccount);
}, [address]);
return (
<div className="App">
<p><code>{ address }</code></p>
<p><code>{ account?.sequence_number }</code></p>
</div>
);
}
Теперь, помимо отображения адреса учетной записи, приложение будет также отображать sequence_number
учетной записи. Этот sequence_number
представляет собой порядковый номер следующей транзакции для предотвращения атак воспроизведения транзакций. Вы увидите, что этот номер увеличивается по мере совершения операций с учетной записью.
Шаг 4: Публикация модуля Move
Теперь наш dapp настроен на чтение из блокчейна. Следующим шагом будет запись в блокчейн. Для этого мы опубликуем модуль Move на нашей учетной записи.
Модуль Move предоставляет место для хранения этих данных. В частности, мы будем использовать модуль HelloBlockchain
из вашего первого модуля Move, который предоставляет ресурс MessageHolder
, хранящий строку (называемую message
).
Публикация модуля HelloBlockchain
с помощью Aptos CLI
Мы будем использовать Aptos CLI для компиляции и публикации модуля HelloBlockchain
.
- Скачайте
hello_blockchain
package. - Затем используйте команду
aptos move publish
(заменив/path/to/hello_blockchain/
и<address>
):
$ aptos move publish --package-dir /path/to/hello_blockchain/ --named-addresses HelloBlockchain=<address>
Например:
$ aptos move publish --package-dir ~/code/aptos-core/aptos-move/move-examples/hello_blockchain/ --named-addresses HelloBlockchain=0x5af503b5c379bd69f46184304975e1ef1fa57f422dd193cdad67dc139d532481
Параметр --named-addresses
заменяет именованный адрес HelloBlockchain
в HelloBlockchain.move
на указанный адрес. Например, если мы укажем --named-addresses HelloBlockchain=0x5af503b5c379bd69f46184304975e1ef1fa57f422dd193cdad67dc139d532481
, то произойдет следующее:
module HelloBlockchain::Message {
станет:
module 0x5af503b5c379bd69f46184304975e1ef1fa57f422dd193cdad67dc139d532481::Message {
Это позволяет опубликовать модуль для данной учетной записи (в данном случае нашей учетной записи кошелька, 0x5af503b5c379bd69f46184304975e1ef1fa57f422dd193cdad67dc139d532481
).
Предполагая, что на вашей учетной записи достаточно средств для выполнения транзакции, теперь вы можете опубликовать модуль HelloBlockchain
в своей учетной записи. Если вы обновите приложение, то увидите, что порядковый номер счета увеличился с 0 до 1.
Вы также можете проверить, что модуль был опубликован, зайдя в Aptos Explorer и найдя свою учетную запись. Если вы прокрутите вниз до раздела "Модули учетной записи", вы должны увидеть что-то вроде следующего:
{
"address": "0x5af503b5c379bd69f46184304975e1ef1fa57f422dd193cdad67dc139d532481",
"name": "Message",
"friends": [],
"exposedFunctions": [
{
"name": "get_message",
"visibility": "public",
"genericTypeParams": [],
"params": [
"address"
],
"_return": [
"0x1::string::String"
]
},
{
"name": "set_message",
"visibility": "script",
"genericTypeParams": [],
"params": [
"signer",
"vector"
],
"_return": []
}
],
"structs": [
{
"name": "MessageChangeEvent",
"isNative": false,
"abilities": [
"drop",
"store"
],
"genericTypeParams": [],
"fields": [
{
"name": "from_message",
"type": "0x1::string::String"
},
{
"name": "to_message",
"type": "0x1::string::String"
}
]
},
{
"name": "MessageHolder",
"isNative": false,
"abilities": [
"key"
],
"genericTypeParams": [],
"fields": [
{
"name": "message",
"type": "0x1::string::String"
},
{
"name": "message_change_events",
"type": "0x1::event::EventHandle<0x5af503b5c379bd69f46184304975e1ef1fa57f422dd193cdad67dc139d532481::Message::MessageChangeEvent>"
}
]
}
]
}
Запишите "name": "Message"
, мы будем использовать его в следующем разделе.
Добавьте инструкции по публикации модулей в dapp
Для удобства пользователей мы можем заставить приложение отображать команду aptos move publish
, если модуль не существует. Для этого мы воспользуемся Aptos SDK для получения модулей учетных записей и поищем тот, в котором module.abi.name
равен "Message"
(т.е. "name": "Message"
, который мы видели в Aptos Explorer).
Обновите src/App.tsx
:
function App() {
// ...
// Check for the module; show publish instructions if not present.
const [modules, setModules] = React.useState<Types.MoveModule[]>([]);
React.useEffect(() => {
if (!address) return;
client.getAccountModules(address).then(setModules);
}, [address]);
const hasModule = modules.some((m) => m.abi?.name === 'Message');
const publishInstructions = (
<pre>
Run this command to publish the module:
<br />
aptos move publish --package-dir /path/to/hello_blockchain/
--named-addresses HelloBlockchain={address}
</pre>
);
return (
<div className="App">
{!hasModule && publishInstructions}
</div>
);
}
Новые пользователи смогут использовать эту команду для создания страницы для своей учетной записи.
Шаг 5. Записать сообщение в блокчейне
Теперь, когда модуль опубликован, мы готовы использовать его для записи сообщения в блокчейн. Для этого мы будем использовать функцию set_message
, открытую модулем.
Транзакция, вызывающая функцию set_message
Подпись для set_message
выглядит следующим образом:
public(script) fun set_message(account: signer, message_bytes: vector<u8>)
Чтобы вызвать эту функцию, нам нужно использовать API window.aptos
, предоставляемый кошельком для отправки транзакции. В частности, мы создадим транзакцию script_function_payload
, которая будет выглядеть следующим образом:
{
type: "script_function_payload",
function: "<address>::Message::set_message",
arguments: ["<hex encoded utf-8 message>"],
type_arguments: []
}
Нет необходимости указывать аргумент account: signer
. Aptos предоставляет его автоматически.
Однако нам необходимо указать аргумент message_bytes
: это "<hex encoded utf-8 message>"
в транзакции. Нам нужен способ преобразовать строку JS в этот формат. Мы можем сделать это, используя TextEncoder
для преобразования в байты utf-8, а затем однострочное предложение для шестнадцатеричной кодировки байтов.
Добавьте эту функцию в src/App.tsx
:
/** Convert string to hex-encoded utf-8 bytes. */
function stringToHex(text: string) {
const encoder = new TextEncoder();
const encoded = encoder.encode(text);
return Array.from(encoded, (i) => i.toString(16).padStart(2, "0")).join("");
}
Используя эту функцию, наша полезная нагрузка транзакции становится:
{
type: "script_function_payload",
function: "<address>::Message::set_message",
arguments: [stringToHex(message)],
type_arguments: []
}
Используйте API window.aptos
для отправки транзакции set_message
Теперь, когда мы поняли, как использовать транзакцию для вызова функции set_message
, мы вызовем эту функцию из нашего приложения с помощью window.aptos.signAndSubmitTransaction()
.
Мы добавим:
- где
<textarea>
пользователь может ввести сообщение, и -
<button>
, который вызываетset_message
функцию с содержимым файла<textarea>
.
Обновите src/App.tsx
:
function App() {
// ...
// Call set_message with the textarea value on submit.
const ref = React.createRef<HTMLTextAreaElement>();
const [isSaving, setIsSaving] = React.useState(false);
const handleSubmit = async (e: any) => {
e.preventDefault();
if (!ref.current) return;
const message = ref.current.value;
const transaction = {
type: "script_function_payload",
function: `${address}::Message::set_message`,
arguments: [stringToHex(message)],
type_arguments: [],
};
try {
setIsSaving(true);
await window.aptos.signAndSubmitTransaction(transaction);
} finally {
setIsSaving(false);
}
};
return (
<div className="App">
{hasModule ? (
<form onSubmit={handleSubmit}>
<textarea ref={ref} />
<input disabled={isSaving} type="submit" />
</form>
) : publishInstructions}
</div>
);
}
Для проверки:
- Введите что-нибудь в
<textarea>
и отправьте форму. - Найдите свою учетную запись в Aptos Explorer, и теперь вы увидите ресурс
MessageHolder
в разделе Account Resources с написанным вамиmessage
.
Если вы его не видите, попробуйте использовать более короткое сообщение. Длинные сообщения могут привести к сбою транзакции, поскольку длинные сообщения требуют больше газа.
Шаг 6. Отображение сообщения в dapp
Теперь, когда ресурс MessageHolder
создан, мы можем использовать Aptos SDK для его получения и отображения сообщения.
Получить сообщение о состоянии учетной записи кошелька
Чтобы получить сообщение, мы сделаем следующее:
- Сначала воспользуемся функцией
AptosClient.getAccountResources()
для получения ресурсов учетной записи и хранения их в состоянии. - Затем мы будем искать тот,
type
которогоMessageHolder
. Полный тип -$address::Message::MessageHolder
, поскольку он является частью модуля$address::Message
.
В нашем примере это:
0x5af503b5c379bd69f46184304975e1ef1fa57f422dd193cdad67dc139d532481::Message::MessageHolder
- Мы будем использовать его для начального значения
<textarea>
.
Обновите src/App.tsx
:
function App() {
// ...
// Get the message from account resources.
const [resources, setResources] = React.useState<Types.AccountResource[]>([]);
React.useEffect(() => {
if (!address) return;
client.getAccountResources(address).then(setResourdces);
}, [address]);
const resourceType = `${address}::Message::MessageHolder`;
const resource = resources.find((r) => r.type === resourceType);
const data = resource?.data as {message: string} | undefined;
const message = data?.message;
return (
// ...
<textarea ref={ref} defaultValue={message} />
// ...
);
}
Для проверки:
- Обновите страницу и увидите сообщение, которое вы написали ранее.
- Измените текст, отправьте форму и снова обновите страницу. Вы увидите, что содержимое страницы было обновлено вашим новым сообщением.
Это подтверждает, что вы читаете и записываете сообщения на блокчейне Aptos.
Отображение сообщений с других учетных записей
На данный момент мы создали "однопользовательский" dapp, в котором вы можете читать и писать сообщения на своей учетной записи. Далее мы сделаем так, чтобы другие люди могли читать сообщения, включая тех, у кого не установлен Aptos Wallet.
Мы настроим его так, чтобы переход по URL /<account address>
отображал сообщение, хранящееся по адресу /<account address>
(если оно существует).
- Если приложение загружено по адресу
/<account address>
, мы также отключим редактирование. - Если редактирование включено, мы покажем ссылку "Get public URL", чтобы вы могли поделиться своим сообщением
Обновите src/App.tsx
:
function App() {
// Retrieve aptos.account on initial render and store it.
const urlAddress = window.location.pathname.slice(1);
const isEditable = !urlAddress;
const [address, setAddress] = React.useState<string | null>(null);
React.useEffect(() => {
if (urlAddress) {
setAddress(urlAddress);
} else {
window.aptos.account().then((data : {address: string}) => setAddress(data.address));
}
}, [urlAddress]);
// ...
return (
<div className="App">
{hasModule ? (
<form onSubmit={handleSubmit}>
<textarea ref={ref} defaultValue={message} readOnly={!isEditable} />
{isEditable && (<input disabled={isSaving} type="submit" />)}
{isEditable && (<a href={address!}>Get public URL</a>)}
</form>
) : publishInstructions}
</div>
);
}
На этом мы завершаем данное руководство.
Top comments (0)