🌟

【Aptos/Move】Transaction発行してみよう!

2024/04/27に公開

はじめに

Aptosを手を動かしながら理解したい人のために、チュートリアルを自分で試してみて、事前に知っておいた方が良いことやつまづきポイントなどまとめました。私も初めて触るので、誤ったことがあればドシドシご指摘ください!

知っておかないと、今後のチュートリアルで理解が難しくなるAptosならではのことについてはこちらに記載しているので、詰まったら参照してみてください!
https://zenn.dev/mameta29/articles/7844978c45f8a3

今回のチュートリアルでは、Aptosブロックチェーンにトランザクションを生成して送信し、送信されたトランザクションを検証する方法について説明します。このチュートリアルで使用される送金コインの例は、Aptos SDKを使用して構築されています。
https://aptos.dev/tutorials/your-first-transaction/

Step 1: SDKを選ぶ

本チュートリアルではSDKはTypeScript、Python、Rustが選べますが、TypeScriptを選択します。
インストールは下記の手順(npmyarnなど)でできますが、私はpnpmを使用していきます。
(以降チュートリアルではpnpmが使用されています。)
TypeSctipt SDKインストール
https://aptos.dev/sdks/ts-sdk/
pnpm インストール
https://pnpm.io/ja/installation

Step 2: Exampleを実行してみよう!

それではコードを動かしてみましょう!

  • まずgithubからコードをcloneしてきます。
git clone https://github.com/aptos-labs/aptos-ts-sdk.git
  • cloneできたらプロジェクトに移り、依存関係をインストールしてビルドします。
cd aptos-ts-sdk
pnpm install
pnpm build

私はここでこけました。なにやらESMとCJSのビルドは成功したが、DTSのビルド中(TypeScriptの型定義ファイル(.d.tsファイル)を生成するプロセス)にメモリ不足エラーが発生してしまいました。

根本的な解決にはなりませんが一旦下記でビルドまで出来ました。
(ヒープメモリの上限を8GBに設定するというフラグを追加)

node --max-old-space-size=8192 ./node_modules/.bin/tsup
  • ビルドができたらプロジェクトディレクトリへ移動します。
cd examples/typescript
  • そこでもう一度依存関係をインストールしてプロジェクトを立ち上げます。
pnpm install
pnpm run transfer_coin

下記のようなアウトプットが出て来れば成功です!

Step 3: アウトプットの理解

それではアウトプットを理解しましょう。下記の手順で実行がされています。

  1. まずAptos clientの初期化 (Aptosチェーンに接続する準備だと思っていただいてよいかと)
  2. AliceとBobのアカウントを作成
  3. faucetからAliceのアカウントにトークンを支給
  4. 1000000 トークンをAliceからBobに送金
  5. Aliceはその際にガス代も支払っている

Step 4: SDKの中身を見てみよう!

それでは実際にコードを見ていきます!こちらのコードを確認していきます。
https://github.com/aptos-labs/aptos-ts-sdk/blob/main/examples/typescript/transfer_coin.ts

Step 4.1: Clientの初期化

最初のステップで、Aptosクライアントを初期化します。

// Setup the client
const APTOS_NETWORK: Network = NetworkToNetworkName[process.env.APTOS_NETWORK] || Network.DEVNET;
const config = new AptosConfig({ network: APTOS_NETWORK });
const aptos = new Aptos(config)

Step 4.2: ローカルでアカウントを作成

次のステップで、ローカルに2つのアカウントを作成しています。

// Create two accounts
const alice = Account.generate();
const bob = Account.generate()

Aptosのアカウントにはオンチェーン状態とオフチェーン状態があるそうです。
オフチェーン状態とは、アカウントのアドレスと、そのアカウントの所有権を認証するための公開鍵/秘密鍵のペアのことを指します。つまり、オフチェーン状態はブロックチェーン上には存在せず、ユーザーがローカルで保持する情報です。
一方、オンチェーン状態はブロックチェーン上に実際に記録され、取引の履歴やアカウントの残高などが含まれます。
このステップでではローカルでアカウントを作成し、オフチェーン状態(アドレスと鍵のペア)を生成しており、オンチェーンにはまだアカウントはない状態です。

Step 4.3: ブロックチェーン・アカウントの作成

ここで、オフチェーンアカウントをブロックチェーン上に実際のオンチェーンアカウントとして作成しています。

  // Fund alice account
  await aptos.fundAccount({
    accountAddress: alice.accountAddress,
    amount: ALICE_INITIAL_BALANCE,
  });

ここでは具体的に以下のようなことをしています。

  • Aliceのアカウントをブロックチェーン上に作成し、Faucetから資金を入金
  • Bobのアカウントをブロックチェーン上に作成するが、資金は入金しない

Step 4.4: Balanceの取得

この部分では、作成したアカウント(AliceとBob)の残高を確認しています。

// Show the balances
console.log("\n=== Initial Balances ===\n");
const aliceBalance = await balance("Alice", alice.accountAddress);
const bobBalance = await balance("Bob", bob.accountAddress);

// balance関数の定義
const balance = async (name: string, accountAddress: AccountAddress, versionToWaitFor?: bigint): Promise<number> => {
  const amount = await aptos.getAccountAPTAmount({
    accountAddress,
    minimumLedgerVersion: versionToWaitFor,
  });
  console.log(`${name}'s balance is: ${amount}`);
  return amount;
};

具体的な手順は以下の通りです。

  1. balance関数を定義しています。この関数は、指定されたアカウントアドレスに対応するAPTコインの残高を取得し、コンソールに出力し、残高の値を返します。
  2. balance関数の中で、Aptos SDKのgetAccountAPTAmountを使って残高を取得しています。getAccountAPTAmountはIndexerサービスにクエリを送り、そのアカウントの現在の残高を読み取ります。
  3. aliceBalanceとbobBalanceという変数に、balance関数を使ってAliceとBobのアカウントの残高を取得し、代入しています。
  4. balance関数の中で、取得した残高と、アカウント名(Alice、Bob)をコンソールに出力しています。

Step 4.5: 送金

この部分では、Aliceから Bobへコインを送金するトランザクションを構築し、ブロックチェーンに送信する処理をしています。

  // Transfer between users
  console.log(`\n=== Transfer ${TRANSFER_AMOUNT} from Alice to Bob ===\n`);
  const transaction = await aptos.transferCoinTransaction({
    sender: alice.accountAddress,
    recipient: bob.accountAddress,
    amount: TRANSFER_AMOUNT,
  });
  const pendingTxn = await aptos.signAndSubmitTransaction({ signer: alice, transaction });

SDKはtransferCoinTransactionトランザクションを生成するヘルパー関数を提供し、これをシミュレートしたり、chainに送信したりすることができる。トランザクションがチェーンに送信されると、API はトランザクションハッシュを返し、それを後続のステップで使用してトランザクションのステータスを確認することができる。Aptosブロックチェーンは送信時にいくつかの検証チェックを行い、そのいずれかが失敗した場合、ユーザーには代わりにエラーが表示される。これらの検証はトランザクションの署名と未使用のシーケンス番号を使用し、適切なチェーンにトランザクションを送信する。

coin.ts
export async function transferCoinTransaction(args: {
  aptosConfig: AptosConfig;
  sender: AccountAddressInput;
  recipient: AccountAddressInput;
  amount: AnyNumber;
  coinType?: MoveStructId;
  options?: InputGenerateTransactionOptions;
}): Promise<SimpleTransaction> {
  const { aptosConfig, sender, recipient, amount, coinType, options } = args;
  const coinStructType = coinType ?? APTOS_COIN;
  return generateTransaction({
    aptosConfig,
    sender,
    data: {
      function: "0x1::aptos_account::transfer_coins",
      typeArguments: [coinStructType],
      functionArguments: [recipient, amount],
      abi: coinTransferAbi,
    },
    options,
  });
}

順を追って実行の流れを見ます。

  1. aptos.transferCoinTransaction()関数を使って、AliceからBobへ指定された金額のコインを送金するトランザクションを生成しています。
  2. 生成されたトランザクションは、aptos.signAndSubmitTransaction()関数を使ってAliceの秘密鍵で署名され、ブロックチェーンに送信(submit)されます。
  3. トランザクションが正常にブロックチェーンに送信されると、そのトランザクションのハッシュ値が返されます。
  4. transferCoinTransaction()関数の内部では、Moveのtransfer_coinsエントリー関数を呼び出すトランザクションのペイロードを生成しています。
  5. transfer_coins関数は、Aptos Accountモジュール(0x1::aptos_account)に定義されています。
  6. この関数は、Coinモジュールを使ってコインの送金を行います。
  7. 送金するコインの種類は、coinType引数で指定できます。指定しない場合はデフォルトでAptosCoinになります。

ちなみに今回のコードで登場するモジュールは主に2つです。

Aptos Account モジュール (0x1::aptos_account)

このモジュールは、アカウントの作成、残高の管理、コインの送受信などアカウントに関する基本的な機能を提供します。
transfer_coins関数がこのモジュールに定義されています。

Coin モジュール (0x1::coin)

このモジュールは、コインの発行、燃焼、振替などコインそのものの操作に関する機能を提供します。
Aptos Coinなどの具体的なコインタイプは、このCoinモジュールを継承して定義されています。

つまり、transfer_coins関数は以下のような流れで動作します:

  1. transfer_coins関数は、Aptos Accountモジュールに定義されている。
  2. この関数の中で、Coinモジュールの機能が使われ、実際のコイン振替処理が行われる。
  3. 振替対象のコインタイプ(Aptos Coinなど)は、Coinモジュールを継承して定義されている。

このように、Moveモジュールは継承やインポートによって連携しながら、さまざまな機能を提供するように設計されています。今回のtransfer_coinsの例では、アカウント管理とコイン管理の2つのモジュールが協調して動作しています。

Step 4.6: トランザクションの処理

この部分では、前のステップで送信したトランザクションが正常に処理されるのを待ち、その結果を取得しています。具体的には、aptos.waitForTransaction()関数を使用しています。この関数には、待機対象のトランザクションハッシュ値を引数として渡します。

 const response = await aptos.waitForTransaction({ transactionHash: pendingTxn.hash });

pendingTxn.hashは、前のステップで送信したトランザクションのハッシュ値です。

waitForTransaction()関数は、以下のような動作をします

  1. 指定されたトランザクションハッシュに対応するトランザクションが、ブロックチェーン上で処理され完了するのを待ちます。
  2. トランザクションが正常に処理された場合は、そのトランザクション情報を返します。
  3. トランザクションの処理中にエラーが発生した場合は、例外(エラー)を投げます。
  4. 一定時間が経過してもトランザクションの処理が完了しない場合は、タイムアウトエラーを投げます。

非同期で送信したトランザクションの最終的な処理結果(成功または失敗)を待ち受けて取得することができます。

まとめ

以上で私自身初めてAptosでtxを実行してみました!実際に手を動かすとわかりやすいです。Aptosのチュートリアルをベースに一通り機能を触っていきたいと思います。

Discussion