Руководство для разработчиков APTOS | Ваш первый модуль MOVE | Typescript
Tags: blockchain, aptos, tutorial, developer
Published: 2022-08-23 12:25:00

В этом руководстве подробно описано, как писать, скомпилировать, тестировать, публиковать и взаимодействовать с модулями Move на блокчейне Aptos. Шаги таковы:

1. Написать, скомпилировать и протестировать Move-модуль.
2. Публикация модуля Move в блокчейн Aptos
3. Инициализировать ресурсы модуля Move и взаимодействовать с ними

Это руководство основано на [вашей первой транзакции](https://dev.to/egormajj/rukovodstvo-dlia-razrabotchikov-aptos-vasha-piervaia-tranzaktsiia-8fk) в качестве библиотеки для данного примера. Следующее руководство содержит код примера, который можно полностью загрузить ниже:

В этом руководстве мы сосредоточимся на `hello_blockchain.ts` и повторно используем библиотеку `first_transaction.ts` из предыдущего руководства.

Вы можете найти проект typescript [здесь](https://aptos.dev/tutorials/your-first-move-module#:~:text=the%20typescript%20project-,here,-Step%201)

## Шаг 1. Напишите и протестируйте модуль Move

**Шаг 1.1 Загрузите Aptos-core**

Для простоты этого упражнения в Aptos-core есть каталог `move-examples`, который позволяет легко создавать и тестировать модули Move без загрузки дополнительных ресурсов. Со временем мы расширим этот раздел, чтобы описать, как использовать инструменты [Move](https://aptos.dev/tutorials/your-first-move-module#:~:text=how%20to%20leverage-,Move,-tools%20for%20development) для разработки.

А пока скачайте и подготовьте Aptos-core:

```javascript 
git clone https://github.com/aptos-labs/aptos-core.git
cd aptos-core
./scripts/dev_setup.sh
source ~/.cargo/env
git checkout origin/devnet
```
Установите утилиту командной строки Aptos. Узнайте больше об [утилите командной строки Aptos](https://aptos.dev/tutorials/your-first-move-module#:~:text=Aptos%20command%20line%20tool)

```javascript
cargo install --git https://github.com/aptos-labs/aptos-core.git aptos
```
**Шаг 1.2 Обзор модуля**

В этом терминале измените содержимое каталогов на `aptos-move/move-examples/hello_blockchain`. Сохраните это окно терминала до конца этого руководства - позже мы будем называть его "Окно Move". В этом разделе мы рассмотрим файл `sources/hello_blockchain.move`.

Этот модуль позволяет пользователям создавать ресурс `String` под своей учетной записью и устанавливать его. Пользователи могут устанавливать только свой ресурс и не могут устанавливать чужие ресурсы.

```javascript
module HelloBlockchain::Message {
    use std::string;
    use std::error;
    use std::signer;

    struct MessageHolder has key {
        message: string::String,
    }

    public entry fun set_message(account: signer, message_bytes: vector<u8>)
    acquires MessageHolder {
        let message = string::utf8(message_bytes);
        let account_addr = signer::address_of(&account);
        if (!exists<MessageHolder>(account_addr)) {
            move_to(&account, MessageHolder {
                message,
            })
        } else {
            let old_message_holder = borrow_global_mut<MessageHolder>(account_addr);
            old_message_holder.message = message;
        }
    }
}
```
В приведенном выше коде две важные секции - это структура `MessageHolder` и функция `set_message`. `set_message` - это `script` функция, позволяющая вызывать ее непосредственно транзакциями. При ее вызове функция определяет, есть ли у текущей учетной записи ресурс `MessageHolder`, и создает и сохраняет `message`, если его нет. Если ресурс существует, `message` в `MessageHolder` перезаписывается.

**Шаг 1.3 Тестирование модуля**

Move позволяет проводить встроенные тесты, поэтому мы добавляем `get_message` для удобства получения `message` и тестовую функцию `sender_can_set_message` для проверки end-to-end процесса . Это можно проверить, запустив `cargo test`. В папке `sources/hello_blockchain_test.move` есть еще один тест, который демонстрирует другой метод написания тестов.

Его можно проверить, введя в терминале команду `cargo test test test_hello_blockchain -p move-examples -- --exact`.

Примечание: `sender_can_set_message` - это `script` функция для вызова `script` функции `set_message`.

```javascript
    const ENO_MESSAGE: u64 = 0;

    public fun get_message(addr: address): string::String acquires MessageHolder {
        assert!(exists<MessageHolder>(addr), Errors::not_published(ENO_MESSAGE));
        *&borrow_global<MessageHolder>(addr).message
    }

    #[test(account = @0x1)]
    public(script) fun sender_can_set_message(account: signer) acquires MessageHolder {
        let addr = Signer::address_of(&account);
        set_message(account,  b"Hello, Blockchain");

        assert!(
          get_message(addr) == string::utf8(b"Hello, Blockchain"),
          0
        );
    }
```

## Шаг 2. Публикация и взаимодействие с модулем Move

Теперь мы возвращаемся к нашему приложению, чтобы развернуть модуль на блокчейне Aptos и взаимодействовать с ним. Как упоминалось ранее, это руководство основывается на предыдущем руководстве и использует общий код. В результате в этом руководстве обсуждаются только новые возможности этой библиотеки, включая возможность публикации, отправку транзакции `set_message` и чтение `MessageHolder::message`. Единственным отличием от публикации модуля и отправки транзакции является тип полезной нагрузки. См. следующее:

**Шаг 2.1 Публикация модуля Move**

```javascript
const client = new AptosClient(NODE_URL);
const faucetClient = new FaucetClient(NODE_URL, FAUCET_URL);

/** Publish a new module to the blockchain within the specified account */
export async function publishModule(accountFrom: AptosAccount, moduleHex: string): Promise<string> {
  const moduleBundlePayload = new TxnBuilderTypes.TransactionPayloadModuleBundle(
    new TxnBuilderTypes.ModuleBundle([new TxnBuilderTypes.Module(new HexString(moduleHex).toUint8Array())]),
  );

  const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
    client.getAccount(accountFrom.address()),
    client.getChainId(),
  ]);

  const rawTxn = new TxnBuilderTypes.RawTransaction(
    TxnBuilderTypes.AccountAddress.fromHex(accountFrom.address()),
    BigInt(sequenceNumber),
    moduleBundlePayload,
    1000n,
    1n,
    BigInt(Math.floor(Date.now() / 1000) + 10),
    new TxnBuilderTypes.ChainId(chainId),
  );

  const bcsTxn = AptosClient.generateBCSTransaction(accountFrom, rawTxn);
  const transactionRes = await client.submitSignedBCSTransaction(bcsTxn);

  return transactionRes.hash;
}
```

> **СОВЕТ**
> Для инициализации модуля можно написать функцию `init_module`. Эта приватная функция выполняется автоматически при публикации модуля. Эта функция `init_module` должна быть приватной, принимать в качестве параметра только signer или ссылку на signer и не возвращать никакого значения. Вот пример:
> ```javascript
>  fun init_module(creator: &signer) {
>         move_to(
>             creator,
>             ModuleData { global_counter: 0 }
>         );
>     }
> ```

**Шаг 2.2 Чтение ресурса**

Модуль публикуется по адресу. Ниже приведен адрес `contract_address`. Это похоже на предыдущий пример, где `Coin` находится по адресу `0x1`. Адрес `contract_address` будет таким же, как и у учетной записи, которая его публикует.

```javascript 
/** Retrieve the resource Message::MessageHolder::message */
async function getMessage(contractAddress: HexString, accountAddress: MaybeHexString): Promise<string> {
  try {
    const resource = await client.getAccountResource(
      accountAddress,
      `${contractAddress.toString()}::message::MessageHolder`,
    );
    return (resource as any).data["message"];
  } catch (_) {
    return "";
  }
}
```
**Шаг 2.3 Изменение ресурса**

Модули Move должны раскрывать функции `script` для инициализации и управления ресурсами. Затем `script` может быть вызван из транзакции.

Примечание: хотя интерфейс REST может отображать строки, из-за ограничений JSON и Move он не может определить, является ли аргумент строкой или строкой в шестнадцатеричном коде. Поэтому аргументы транзакции всегда предполагают последнее. Следовательно, в данном примере сообщение закодировано как шестнадцатеричная строка.

```javascript
/**  Potentially initialize and set the resource Message::MessageHolder::message */
async function setMessage(contractAddress: HexString, accountFrom: AptosAccount, message: string): Promise<string> {
  const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
    TxnBuilderTypes.EntryFunction.natural(
      `${contractAddress.toString()}::message`,
      "set_message",
      [],
      [BCS.bcsSerializeStr(message)],
    ),
  );

  const [{ sequence_number: sequenceNumber }, chainId] = await Promise.all([
    client.getAccount(accountFrom.address()),
    client.getChainId(),
  ]);

  const rawTxn = new TxnBuilderTypes.RawTransaction(
    TxnBuilderTypes.AccountAddress.fromHex(accountFrom.address()),
    BigInt(sequenceNumber),
    entryFunctionPayload,
    1000n,
    1n,
    BigInt(Math.floor(Date.now() / 1000) + 10),
    new TxnBuilderTypes.ChainId(chainId),
  );

  const bcsTxn = AptosClient.generateBCSTransaction(accountFrom, rawTxn);
  const transactionRes = await client.submitSignedBCSTransaction(bcsTxn);

  return transactionRes.hash;
}
```

## Шаг 3. Инициализация и взаимодействие с модулем Move
Для Typescript:

- Загрузите [пример проекта](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/typescript)
- Откройте ваш любимый терминал и перейдите туда, где вы скачали вышеупомянутый пример проекта
- Установите необходимые библиотеки: `yarn install`
- Выполните пример: `yarn hello_blockchain Message.mv`
- Через несколько мгновений появится сообщение: "Обновить модуль с адресом Alice, собрать, скопировать в указанный путь и нажать enter."
- В терминале "Окно Move" и для файла Move, который мы рассматривали ранее:

- Скопировать адрес Alice.
- Скомпилируйте модули с адресом Alice командой `aptos move compile --package-dir . --named-addresses hello_blockchain=0x{alice_address_here}`. Здесь мы заменяем общий именованный адрес `hello_blockchain='_'` в `hello_blockchain/move.toml` на адрес Alice
- Скопируйте `build/Examples/bytecode_modules/Message.mv` в ту же папку, что и код данного руководства проекта.
- Вернитесь в другое окно терминала и нажмите "enter" в подсказке, чтобы продолжить выполнение остальной части кода

Вывод должен выглядеть следующим образом:

```
=== Addresses ===
Alice: 11c32982d04fbcc79b694647edff88c5b5d5b1a99c9d2854039175facbeefb40
Bob: 7ec8f962139943bc41c17a72e782b7729b1625cf65ed7812152a5677364a4f88

=== Initial Balances ===
Alice: 10000000
Bob: 10000000

Update the module with Alice's address, build, copy to the provided path, and press enter.

=== Testing Alice ===
Publishing...
Initial value: None
Setting the message to "Hello, Blockchain"
New value: Hello, Blockchain

=== Testing Bob ===
Initial value: None
Setting the message to "Hello, Blockchain"
New value: Hello, Blockchain

```
Результат показывает, что Alice и Bob прошли путь от отсутствия ресурса до ресурса с `message` "Hello, Blockchain".

Данные можно проверить, посетив либо REST-интерфейс, либо Explorer:

- Учетная запись Alice's через интерфейс [Aptos REST](https://aptos.dev/tutorials/your-first-move-module#:~:text=account%20via%20the-,REST%20interface,-Bob%27s%20account%20on).
- Учетная запись Bob's через [Aptos Explorer](https://aptos.dev/tutorials/your-first-move-module#:~:text=account%20on%20the-,explorer,-Edit%20this%20page).