Транзакции с использованием Typescript SDK
Published: 2022-09-08 14:50:56

В этом руководстве показаны шаги по созданию, подписанию и отправке транзакции в формате BCS с использованием Aptos Typescript SDK.

Отправка транзакций в BCS в сравнении с JSON
BCS: Отправка транзакций в формате BCS более безопасна, чем отправка в формате JSON. В этом методе вы создадите BCS-сериализованное сообщение для подписи на стороне клиента. Подробное руководство по отправке в формате BCS см. в руководстве "[Создание подписанной транзакции](https://dev.to/egormajj/rukovodstvo-po-sozdaniiu-podpisannoi-tranzaktsii-2e41)". Typescript SDK поддерживает подписание и отправку транзакций в формате BCS.

JSON: Если вы отправляете транзакции в формате JSON, вы будете использовать REST API и полагаться на сервер Aptos для создания подписывающего сообщения. Такой подход создает риск того, что пользователь подпишет непреднамеренную транзакцию, подделанную вредоносным сервером API. О том, как отправлять транзакции в формате JSON, читайте в руководстве "[Ваша первая транзакция](https://dev.to/egormajj/rukovodstvo-dlia-razrabotchikov-aptos-vasha-piervaia-tranzaktsiia-8fk)". Кроме того, Typescript SDK предоставляет оболочки, позволяющие значительно сократить объем ручной работы, необходимой для подготовки и отправки транзакций в формате JSON.

> **ПОДСКАЗКА**
> Мы настоятельно рекомендуем вам использовать формат BCS для отправки транзакций в блокчейн Aptos.

## Прежде чем приступить к работе

Прежде чем продолжить, установите последнюю версию Aptos TS SDK. Перейдите в корень каталога вашего проекта и запустите:

`npm install aptos` или `yarn add aptos`

> **ПРИМЕЧАНИЕ**
> Смотрите [исходный код этого руководства](https://github.com/aptos-labs/aptos-core/blob/main/ecosystem/typescript/sdk/examples/typescript/bcs_transaction.ts). Хотя в этом руководстве используется Typescript, Aptos TS SDK также работает в проектах Javascript.

Шаг 1: Создание учетных записей
Предположим, что пользователь Alice хочет отправить 717 пробных coins пользователю Bob. Сначала нам нужно создать две учетные записи пользователей.

```javascript 
import { AptosClient, AptosAccount, FaucetClient, BCS, TxnBuilderTypes } from "aptos";

// devnet is used here for testing
const NODE_URL = "https://fullnode.devnet.aptoslabs.com";
const FAUCET_URL = "https://faucet.devnet.aptoslabs.com";

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

// Generates key pair for Alice
const alice = new AptosAccount();
// Creates Alice's account and mint 5000 test coins
await faucetClient.fundAccount(alice.address(), 5000);

let resources = await client.getAccountResources(alice.address());
let accountResource = resources.find((r) => r.type === "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>");
console.log(`Alice coins: ${(accountResource?.data as any).coin.value}. Should be 5000!`);

// Generates key pair for Bob
const bob = new AptosAccount();
// Creates Bob's account and mint 0 test coins
await faucetClient.fundAccount(bob.address(), 0);

resources = await client.getAccountResources(bob.address());
accountResource = resources.find((r) => r.type === "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>");
console.log(`Bob coins: ${(accountResource?.data as any).coin.value}. Should be 0!`);
```
С помощью приведенного выше кода мы создали две учетные записи на Aptos devnet и заминтили 5000 тестовых coins для учетной записи Alice и 0 тестовых coins для учетной записи Bob.

## Шаг 2. Подготовьте полезную среду транзакции

Typescript SDK поддерживает три типа полезной среды транзакций:

1. `ScriptFunction`
2. `Script` и
3. `ModuleBundle`.

Подробности см. на сайте https://aptos-labs.github.io/ts-sdk-doc/classes/TxnBuilderTypes.TransactionPayload.html.

Полезная среда `ScriptFunction` используется для вызова функции скрипта Move на цепочке. В полезной среде `ScriptFunction` вы можете указать имя функции и аргументы.

Полезная среда `Script` содержит байткод для выполнения Aptos MoveVM (Move Virtual Machine). В полезной среде `Script` можно указать код сценария в байтах и аргументы сценария.

Полезная нагрузка `ModuleBundle` используется для публикации нескольких модулей одновременно. В полезной нагрузке `ModuleBundle` вы можете предоставить байткод модуля.

Для перевода coins с учетной записи Alice на учетную запись Bob нам необходимо подготовить полезную среду `ScriptFunction` с функцией `transfer`.

```javascript
// We need to pass a token type to the `transfer` function.
const token = new TxnBuilderTypes.TypeTagStruct(TxnBuilderTypes.StructTag.fromString("0x1::aptos_coin::AptosCoin"));

const entryFunctionPayload = new TxnBuilderTypes.TransactionPayloadEntryFunction(
  TxnBuilderTypes.EntryFunction.natural(
    // Fully qualified module name, `AccountAddress::ModuleName`
    "0x1::coin",
    // Module function
    "transfer",
    // The coin type to transfer
    [token],
    // Arguments for function `transfer`: receiver account address and amount to transfer
    [BCS.bcsToBytes(TxnBuilderTypes.AccountAddress.fromHex(bob.address())), BCS.bcsSerializeUint64(717)],
  ),
);
```
Функция Move `transfer` требует тип coin в качестве аргумента type. Функция `transfer` определена здесь https://github.com/aptos-labs/aptos-core/blob/faf4f94260d4716c8a774b3c17f579d203cc4013/aptos-move/framework/aptos-framework/sources/Coin.move#L311.

В приведенном выше фрагменте кода мы хотим передать `AptosCoin`, который определен под учетной записью `0x1` и модулем `AptosCoin`. Поэтому полное имя `AptosCoin` будет `0x1::aptos_coin::AptosCoin`.

> **ПРИМЕЧАНИЕ**
> Все аргументы в полезной среде ScriptFunction должны быть сериализованы BCS. В приведенном выше коде мы сериализовали адрес учетной записи Bob'а и номер суммы для перевода.

Шаг 3. Подпишите и отправьте транзакцию

После сборки полезной нагрузки транзакции мы готовы создать экземпляр `RawTransaction`, который обертывает только что созданную полезную нагрузку. Затем `RawTransaction` можно подписать и отправить.

```javascript
// Create a raw transaction out of the transaction payload
const rawTxn = await client.generateRawTransaction(alice.address(), entryFunctionPayload);

// Sign the raw transaction with Alice's private key
const bcsTxn = AptosClient.generateBCSTransaction(alice, rawTxn);
// Submit the transaction
const transactionRes = await client.submitSignedBCSTransaction(bcsTxn);

// Wait for the transaction to finish
await client.waitForTransaction(transactionRes.hash);

resources = await client.getAccountResources(bob.address());
accountResource = resources.find((r) => r.type === "0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>");
console.log(`Bob coins: ${(accountResource?.data as any).coin.value}. Should be 717!`);
```

## Результат
Результат после выполнения:

```
Alice coins: 5000. Should be 5000!
Bob coins: 0. Should be 0!
Bob coins: 717. Should be 717!
```

## Создание необработанных транзакций с помощью конструктора транзакций ABI

Чтобы уменьшить бремя сериализации аргументов полезной нагрузки вручную, Typescript SDK также предоставляет конструктор транзакций на основе ABI (Application Binary Interface). ABI включает информацию о сигнатурах функций Move. С помощью ABI, Typescript SDK может сериализовать собственные значения JS/TS. Файлы ABI создаются при компиляции пакетов Move с помощью Aptos CLI. Для создания необработанных транзакций с помощью ABI transaction builder сначала необходимо преобразовать ABI-файлы в шестнадцатеричные строки. На linux/Mac это можно сделать с помощью следующей команды:

```javascript
cat aptos-core/aptos-move/framework/aptos-token/build/AptosToken/abis/token_transfers/offer_script.abi | od -v -t x1 -A n | tr -d ' \n'
```
А затем вы можете создавать необработанные транзакции:

```javascript
import { TransactionBuilderABI, HexString } from "aptos";

// You can pass in multiple ABIs
const transactionBuilder = new TransactionBuilderABI([
  new HexString("ABI_HEX_STRING_1").toUint8Array(),
  new HexString("ABI_HEX_STRING_2").toUint8Array(),
]);

const rawTransaction = transactionBuilder.build(
  "0x3::token_transfers::offer_script",
  [],
  [receiver, creator, collectionName, name, property_version, amount],
);
```
После создания необработанных транзакций вы можете выполнить описанный выше Шаг 3, чтобы подписать и отправить транзакцию.