# Ваш первый Move модуль

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

1. Напишите, скомпилируйте и протестируйте модуль Move.
2. Опубликуйте модуль Move в Aptos Blockchain.
3. Запустите и взаимодействуйте с ресурсами Move Module.

Это руководство основано на разделе [Ваша первая транзакция](https://cr-nepos.gitbook.io/aptos-ru/rukovodstva-po-primeneniyu/vasha-pervaya-tranzakciya) в качестве библиотеки для этого примера. Данное руководство содержит пример кода, который можно полностью загрузить:

{% tabs %}
{% tab title="Python" %}
В этом руководстве основное внимание будет уделено `hello_blockchain.py` и повторному использованию библиотеки `first_transaction.py` из предыдущего руководства.

Вы найдете python project [здесь](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/python).
{% endtab %}

{% tab title="Rust" %}
В этом руководстве основное внимание будет уделено `hello_blockchain.rs` и повторному использованию библиотеки `first_transaction.rs` из предыдущего руководства.

Вы найдете rust project [здесь](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/rust).
{% endtab %}

{% tab title="Typescript" %}
В этом руководстве основное внимание будет уделено `hello_blockchain.ts` и повторному использованию библиотеки `first_transaction.ts` из предыдущего руководства.

Вы найдете typescript project [здесь](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/typescript).
{% endtab %}
{% endtabs %}

### Шаг 1) Напишите и протестируйте Move Module <a href="#step-1-write-and-test-the-move-module" id="step-1-write-and-test-the-move-module"></a>

#### Шаг 1.1) Загрузите Aptos-core <a href="#step-11-download-aptos-core" id="step-11-download-aptos-core"></a>

Для простоты выполнения этой задачи в Aptos-core имеется каталог `move-examples` , который позволяет легко создавать и тестировать модули Move без загрузки дополнительных ресурсов. Со временем мы расширим этот раздел, чтобы описать, как использовать для разработки инструменты [Move](https://github.com/move-language/move/tree/main/language/documentation/tutorial).

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

```
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 Commandline tool. Узнайте больше по ссылке [Aptos command line tool](https://github.com/aptos-labs/aptos-core/tree/main/crates/aptos).

```
cargo install --git https://github.com/aptos-labs/aptos-core.git aptos
```

#### Шаг 1.2) Просмотрите модуль <a href="#step-12-review-the-module" id="step-12-review-the-module"></a>

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

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

```
module HelloBlockchain::Message {
    use Std::ASCII;
    use Std::Errors;
    use Std::Signer;

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

    public(script) fun set_message(account: signer, message_bytes: vector<u8>)
    acquires MessageHolder {
        let message = ASCII::string(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) Тестирование модуля <a href="#step-13-testing-the-module" id="step-13-testing-the-module"></a>

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

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

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

```
const ENO_MESSAGE: u64 = 0;

    public fun get_message(addr: address): ASCII::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) == ASCII::string(b"Hello, Blockchain"),
          0
        );
    }
```

### Шаг 2) Публикация и взаимодействие с Move Module <a href="#step-2-publishing-and-interacting-with-the-move-module" id="step-2-publishing-and-interacting-with-the-move-module"></a>

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

#### Шаг 2.1) Публикация Move Module <a href="#step-21-publishing-the-move-module" id="step-21-publishing-the-move-module"></a>

{% tabs %}
{% tab title="Python" %}

```
class HelloBlockchainClient(RestClient):
    def publish_module(self, account_from: Account, module_hex: str) -> str:
        """Publish a new module to the blockchain within the specified account"""

        payload = {
            "type": "module_bundle_payload",
            "modules": [
                {"bytecode": f"0x{module_hex}"},
            ],
        }
        txn_request = self.generate_transaction(account_from.address(), payload)
        signed_txn = self.sign_transaction(account_from, txn_request)
        res = self.submit_transaction(signed_txn)
        return str(res["hash"])
```

{% endtab %}

{% tab title="Rust" %}

```
pub struct HelloBlockchainClient {
    pub rest_client: RestClient,
}

impl HelloBlockchainClient {
    /// Represents an account as well as the private, public key-pair for the Aptos blockchain.
    pub fn new(url: String) -> Self {
        Self {
            rest_client: RestClient::new(url),
        }
    }

    /// Publish a new module to the blockchain within the specified account
    pub fn publish_module(&self, account_from: &mut Account, module_hex: &str) -> String {
        let payload = serde_json::json!({
            "type": "module_bundle_payload",
            "modules": [{"bytecode": format!("0x{}", module_hex)}],
        });
        self.rest_client.execution_transaction_with_payload(account_from, payload)
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```
export class HelloBlockchainClient extends RestClient {
  /** Publish a new module to the blockchain within the specified account */
  async publishModule(accountFrom: Account, moduleHex: string): Promise<string> {
    const payload = {
      type: "module_bundle_payload",
      modules: [{ bytecode: `0x${moduleHex}` }],
    };
    const txnRequest = await this.generateTransaction(accountFrom.address(), payload);
    const signedTxn = await this.signTransaction(accountFrom, txnRequest);
    const res = await this.submitTransaction(signedTxn);
    return res["hash"];
  }
  
```

{% endtab %}
{% endtabs %}

#### Шаг 2.2) Чтение ресурса <a href="#step-22-reading-a-resource" id="step-22-reading-a-resource"></a>

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

{% tabs %}
{% tab title="Python" %}

```
  def get_message(self, contract_address: str, account_address: str) -> Optional[str]:
        """ Retrieve the resource Message::MessageHolder::message """
        return self.account_resource(account_address, f"0x{contract_address}::Message::MessageHolder")
```

{% endtab %}

{% tab title=" Rust" %}

```
    /// Retrieve the resource Message::MessageHolder::message
    pub fn get_message(&self, contract_address: &str, account_address: &str) -> Option<String> {
        let module_type = format!("0x{}::Message::MessageHolder", contract_address);
        self.rest_client
            .account_resource(account_address, &module_type)
            .map(|value| value["data"]["message"].as_str().unwrap().to_string())
    }

    
```

{% endtab %}

{% tab title=" Typescript" %}

```
  /** Retrieve the resource Message::MessageHolder::message */
  async getMessage(contractAddress: string, accountAddress: string): Promise<string> {
    const resource = await this.accountResource(accountAddress, `0x${contractAddress}::Message::MessageHolder`);
    if (resource == null) {
      return null;
    } else {
      return resource["data"]["message"];
    }
  }
  
```

{% endtab %}
{% endtabs %}

#### Шаг 2.3) Изменение ресурса <a href="#step-23-modifying-a-resource" id="step-23-modifying-a-resource"></a>

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

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

{% tabs %}
{% tab title="Python" %}

```
    def set_message(self, contract_address: str, account_from: Account, message: str) -> str:
        """ Potentially initialize and set the resource Message::MessageHolder::message """

        payload = {
            "type": "script_function_payload",
            "function": f"0x{contract_address}::Message::set_message",
            "type_arguments": [],
            "arguments": [
                message.encode("utf-8").hex(),
            ]
        }
        res = self.execute_transaction_with_payload(account_from, payload)
        return str(res["hash"])
```

{% endtab %}

{% tab title="Rust" %}

```
    /// Potentially initialize and set the resource Message::MessageHolder::message
    pub fn set_message(
        &self,
        contract_address: &str,
        account_from: &mut Account,
        message: &str,
    ) -> String {
        let message_hex = hex::encode(message.as_bytes());
        let payload = serde_json::json!({
            "type": "script_function_payload",
            "function": format!("0x{}::Message::set_message", contract_address),
            "type_arguments": [],
            "arguments": [message_hex]
        });
        self.rest_client.execution_transaction_with_payload(account_from, payload)
    }
    
```

{% endtab %}

{% tab title="Typescript" %}

```
  /**  Potentially initialize and set the resource Message::MessageHolder::message */
  async setMessage(contractAddress: string, accountFrom: Account, message: string): Promise<string> {
    let payload: { function: string; arguments: string[]; type: string; type_arguments: any[] };
    payload = {
      type: "script_function_payload",
      function: `0x${contractAddress}::Message::set_message`,
      type_arguments: [],
      arguments: [Buffer.from(message, "utf-8").toString("hex")],
    };

    const txnRequest = await this.generateTransaction(accountFrom.address(), payload);
    const signedTxn = await this.signTransaction(accountFrom, txnRequest);
    const res = await this.submitTransaction(signedTxn);
    return res["hash"];
  }
}
```

{% endtab %}
{% endtabs %}

#### Шаг 3) Запуск и взаимодействие с Move module <a href="#step-3-initialize-and-interact-with-the-move-module" id="step-3-initialize-and-interact-with-the-move-module"></a>

{% tabs %}
{% tab title="Python" %}
Для Python3:

* Загрузите [example project](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/python).
* Откройте свой любимый терминал и перейдите туда, куда вы загрузили приведенный выше example project.
* Установите необходимые библиотеки: `pip3 install -r requirements.txt`.
* Выполните: `python3 hello_blockchain.py Message.mv`
  {% endtab %}

{% tab title="Rust" %}
Для Rust:

* Загрузите [example project](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/rust).
* Откройте свой любимый терминал и перейдите туда, куда вы загрузили приведенный выше example project.
* Выполните: `cargo run --bin hello-blockchain -- Message.mv`
  {% endtab %}

{% tab title=" Typescript " %}
Для Typescript:

* Загрузите [example project](https://github.com/aptos-labs/aptos-core/tree/main/developer-docs-site/static/examples/typescript).

* Откройте свой любимый терминал и перейдите туда, куда вы загрузили приведенный выше example project.

* Установите необходимые библиотеки: `yarn install`

* Выполните: `yarn hello_blockchain Message.mv`
  {% endtab %}
  {% endtabs %}

* Через некоторое время появится сообщение: "Обновите модуль с адресом Алисы, создайте, скопируйте по указанному пути и нажмите Enter."

* В терминале "Move Window" и для файла Move, который мы ранее рассматривали:
  * Скопируйте адрес Алисы
  * Скомпилируйте модули с адресом Алисы с помощью `aptos move compile --package-dir . --named-addresses HelloBlockchain=0x{alice_address_here}`. Здесь бы заменяем общий адрес `HelloBlockChain='_'` в `hello_blockchain/move.toml` на адрес Алисы
  * Скопируйте `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
```

Результат показывает, что Алиса и Боб перешли от отсутствия ресурсов к одному с  `message`  "Hello, Blockchain".

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

* Учетная запись Алисы через [REST interface](https://fullnode.devnet.aptoslabs.com/accounts/a52671f10dc3479b09d0a11ce47694c0/)
* Учетная запись Боба на [explorer](https://explorer.devnet.aptos.dev/account/ec6ec14e4abe10aaa6ad53b0b63a1806)
