初めまして。
CryptoGamesというブロックチェーンゲーム企業でエンジニアをしている cardene(かるでね) です!
ERCについてひたすらまとめたり、スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。
以下でも情報発信しているので、興味ある記事があればぜひ読んでみてください!
https://twitter.com/cardene777
https://chaldene.net/
https://qiita.com/cardene
https://zenn.dev/heku
https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58
https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
また、これからも情報を見逃したくないという方はぜひ以下の購読ボタンを教えてください。
更新があった時、登録しているメールアドレス宛に通知が飛ぶようになります。
今回はAptosのスクリプトについてまとめていきます。
Aptosでは、Move言語というプログラミング言語を使用してコントラクトを作成します。
この時スクリプトという仕組みは理解しておく必要があるため、この記事で学んでいってください。
以下のドキュメントをベースにまとめていきます。
https://aptos.dev/move/move-on-aptos/scripts/
Move言語のスクリプトとは?
Moveスクリプトは、Aptosブロックチェーン上で単一のトランザクションに複数の公開関数を実行する方法です。
これは、共通タスクを実行するためのヘルパーモジュールをデプロイするのに似ていますが、事前にデプロイする必要がないという柔軟性があります。
例えば、ユーザーの残高の半分を別のアカウントに転送する機能を考えてみます。
これはプログラム可能なタスクですが、モジュールをデプロイする必要はないでしょう。
以下に示すのは、Moveスクリプトの具体的な例です。
このスクリプトでは、あるアカウントから別のアカウントへ残高の半分を送金する処理を行います。
script {
use std::signer;
use aptos_framework::coin;
use aptos_framework::aptos_account;
fun transfer_half<Coin>(caller: &signer, receiver_address: address) {
// 呼び出し元のアドレスを取得
let caller_address: address = signer::address_of(caller);
// 呼び出し元の残高を取得
let balance: u64 = coin::balance<Coin>(caller_address);
// 受取人に残高の半分を送信
let half = balance / 2;
aptos_account::transfer_coins<Coin>(caller, receiver_address, half);
}
}
このスクリプトでは、以下のステップで機能します。
std::signer
モジュールとaptos_framework
のcoin
およびaptos_account
モジュールを使用します。transfer_half
関数を定義し、トランザクションの発行者 (caller
) と受取人のアドレス (receiver_address
) を引数として取ります。発行者のアドレスと残高を取得します。
残高の半分を計算し、その値を受取人に送金します。
このようなスクリプトは、特定の操作を繰り返す必要があるが、そのためだけにモジュールをデプロイするほどではない場合に有用です。
モジュールとスクリプト
Moveプログラミング言語には、「モジュール」と「スクリプト」という二つの異なるタイプのプログラムがあります。
これらのプログラムの特徴と機能について詳しく解説します。
モジュール
Moveの「モジュール」はライブラリのようなもので、構造体(struct types)とこれらの型に作用する関数を定義します。
構造体はMoveのグローバルストレージのスキーマを定義し、モジュール関数はストレージの更新ルールを定義します。
モジュール自体もグローバルストレージに格納されます。モジュールは、ブロックチェーンの永続的なコードとして保存され、状態やロジックを変更するために利用されます。
スクリプト
一方、「スクリプト」は主に実行用のエントリポイントとして機能します。
これは従来のプログラミング言語における「main」関数に似ています。スクリプトは通常、公開されているモジュールの関数を呼び出してグローバルストレージを更新する操作を行います。
スクリプトは一時的なコードスニペットであり、グローバルストレージには公開されません。
スクリプトは一回限りのトランザクションや特定の操作を実行するために使用され、実行後はブロックチェーンに残りません。
ソースファイルとVM操作
Moveのソースファイル(またはコンパイル単位)は、複数のモジュールやスクリプトを含むことができます。
しかし、モジュールを公開することとスクリプトを実行することは、仮想マシン(VM)の異なる操作です。モジュールの公開は、グローバルストレージにモジュールを永続的に格納するプロセスであり、スクリプトの実行は、一時的なトランザクションやデータ更新を行うためのものです。
このように、Move言語は、モジュールとスクリプトを通じて、ブロックチェーンの状態を効果的に管理および更新するための柔軟なアプローチを提供します。
モジュールがシステムの基盤を形成し、スクリプトが特定の操作や更新を迅速に実行する手段となります。
Moveスクリプトの作成
Moveスクリプトを書くための具体的な手順と構成について解説します。
MoveスクリプトはMoveコントラクトと一緒に書かれることがありますが、スクリプト用に別のMoveパッケージを使用することが推奨されます。
これにより、どのバイトコードファイルがスクリプトから来たのかを容易に判断できます。
パッケージ構成
スクリプトを含むMoveパッケージは、Move.toml
ファイルとsources
ディレクトリを含む必要があります。
これはコードモジュールと似ています。
例えば、以下のようなディレクトリ構成を考えることができます。
my_project/
├── Move.toml
└── sources/
└── my_script.move
スクリプトの構文
Aptosでのモジュールと同様にスクリプトを書くことができます。Move.toml
ファイルにある依存関係をインポートし、すべての公開関数、エントリ関数もコントラクトから呼び出すことができます。
ただし、以下のいくつかの制限があります。
コントラクトには1つの関数のみが含まれている必要があり、その名前でコンパイルされます。
入力引数は
[u8, u16, u32, u64, u256, address, bool, signer, &signer, vector<u8>]
のみをサポートしています。他のタイプのベクターや構造体はサポートされていません。
以下に具体的なスクリプト例を示します。
script {
use std::signer;
use aptos_framework::coin;
use aptos_framework::aptos_account;
fun transfer_half<Coin>(caller: &signer, receiver_address: address) {
// 呼び出し元のアドレスを取得
let caller_address: address = signer::address_of(caller);
// 呼び出し元の残高を取得
let balance: u64 = coin::balance<Coin>(caller_address);
// 受取人に残高の半分を送信
let half = balance / 2;
aptos_account::transfer_coins<Coin>(caller, receiver_address, half);
}
}
このスクリプトは、特定の通貨タイプCoin
に対して、指定されたアドレスにその通貨の残高の半分を送金する機能を持ちます。
このように、MoveスクリプトはAptos上で特定のトランザクションを効率的に実行するための強力なツールとして機能します。
Moveスクリプトのコンパイル
Moveスクリプトのコンパイルには、AptosのCLIツールを使用します。
このツールはAptos Moveコンパイラを内蔵しており、Moveコントラクトの作成や管理に関するさまざまな機能を提供します。
Aptos CLIのインストール方法やMoveコントラクトとの連携については、以下のドキュメントで詳しく説明されています。
https://aptos.dev/tools/aptos-cli/use-cli/working-with-move-contracts/
Moveスクリプトのコンパイル手順
Aptos CLIがインストールされている状態で、スクリプトが含まれるパッケージディレクトリ内で以下のコマンドを実行することで、Moveスクリプトをコンパイルできます。
aptos move compile
このコマンドを実行すると、build/
ディレクトリに関数名と同じ名前のコンパイル済みバイトコードファイルが生成されます。
例えば、transfer_half
パッケージ内のスクリプトは build/transfer_half/bytecode_scripts/transfer_half.mv
としてコンパイルされます。
便利なコマンド
パッケージが一つのスクリプトのみを含む場合は、さらに便利なコマンドが用意されています。
aptos move compile-script
このコマンドを使用すると、スクリプトの正確な位置とハッシュ値が出力され、以下のような結果が得られます。
Compiling, may take a little while to download git dependencies...
UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-core.git
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING transfer_half
{
"Result": {
"script_location": "/opt/git/developer-docs/apps/docusaurus/static/move-examples/scripts/transfer_half/script.mv",
"script_hash": "9b57ffa952da2a35438e2cf7e941ef2120bb6c2e4674d4fcefb51d5e8431a148"
}
}
これにより、スクリプトの正確な位置とハッシュ値を簡単に確認でき、デバッグや検証作業が容易になります。
これは、開発プロセスを効率化し、スクリプトの管理をより簡単にするための非常に有用な機能です。
Moveスクリプトの実行
Moveスクリプトを実行する方法には、Aptos TypeScript SDK、Aptos Wallet Adapter、Aptos CLIを使用する方法があります。
TypeScript SDKを使用したスクリプトの実行
TypeScript SDKを使用してスクリプトを実行する場合、トランザクションにエントリ関数名の代わりにバイトコードを直接追加します。
import { readFileSync } from "fs";
import { Aptos, Account, AccountAddress } from "@aptos-labs/ts-sdk";
// クライアントのセットアップと署名用アカウントの作成
const aptos = new Aptos();
const account = Account.generate();
// スクリプトのバイトコードを読み込む
const buffer = readFileSync("./transfer_half.mv", "buffer");
const bytecode = new Uint8Array.from(buffer);
// スクリプトのバイトコードを含むトランザクションの構築
const transaction = await aptos.transaction.build.simple({
sender: account.accountAddress,
data: {
bytecode,
typeArguments: ["0x1::aptos_coin::AptosCoin"],
functionArguments: ["0x1"],
},
});
// トランザクションの送信と完了を待つ
const pendingTxn = await aptos.signAndSubmitTransaction({
signer: account,
transaction,
});
await aptos.waitForTransaction({ transactionHash: pendingTxn.hash });
Aptos Wallet Adapterを使用したスクリプトの実行
ウォレットアダプタを使用してスクリプトを実行する場合、TypeScript SDKと同様に、トランザクションタイプとして同じ入力を受け入れます。
ただし、ウォレットがスクリプトをサポートしている必要があります。
import { useWallet } from "@aptos-labs/wallet-adapter-react";
// バイトコードをuint8arrayまたは16進エンコードされた文字列として読み込む
const BYTECODE_IN_HEX = "0xa11ceb0b...78979";
export default function App() {
const { signAndSubmitTransaction } = useWallet();
function submitScript() {
signAndSubmitTransaction({
data: {
bytecode: BYTECODE_IN_HEX,
typeArguments: ["0x1::aptos_coin::AptosCoin"],
functionArguments: ["0x1"],
},
});
}
// ...
}
CLIを使用したスクリプトの実行
CLIを使用してスクリプトを実行する場合、コンパイル済みのスクリプトを使用するか、場所によってスクリプトをコンパイルしてから実行します。
コンパイル済みのスクリプトを使用する場合のコマンド。
aptos move run-script --compiled-script-path /opt/git/developer-docs/apps/docusaurus/static/move-examples/scripts/transfer_half/script.mv
未コンパイルのスクリプトを使用する場合のコマンド。
aptos move run-script --script-path ./sources/transfer_half.move
これらの方法を使用することで、Aptos上でMoveスクリプトを柔軟に実行できます。
各方法は異なる状況やニーズに応じて選択することが可能です。
Moveスクリプトのチュートリアル
この例では、Move言語を使用してAptosブロックチェーン上でスクリプトを実行する方法を示しています。
スクリプトの目的は、特定の送金元アカウントの残高を確認し、必要に応じて別のアカウントへ資金を補充することです。
具体的には、送金元の残高が希望する残高 desired_balance
未満の場合に、差額をそのアカウントに転送します。
スクリプトの詳細な説明
script {
use std::signer;
use aptos_framework::aptos_account;
use aptos_framework::aptos_coin;
use aptos_framework::coin;
// スクリプトのメイン関数
fun main(src: &signer, dest: address, desired_balance: u64) {
// 送金元アカウントのアドレスを取得
let src_addr = signer::address_of(src);
// my_moduleモジュールのdo_nothing関数を呼び出す(実際の操作はなし)
addr::my_module::do_nothing();
// 送金元のAptosCoinの残高を取得
let balance = coin::balance
の<AptosCoin>(src_addr);
// 残高が希望する残高未満の場合に実行
if (balance < desired_balance) {
// 不足分を宛先アカウントに転送
aptos_account::transfer(src, dest, desired_balance - balance);
};
}
}
コードの流れ
モジュールの使用宣言
スクリプトは
std::signer
、aptos_framework::aptos_account
、aptos_framework::aptos_coin
、aptos_framework::coin
を使用しています。
これにより、署名者の操作、アカウント関連の機能、Aptosコインに関連する機能を利用できます。関数の定義と引数
main
関数は、署名者(送金元)、宛先アドレス、そして希望する残高を引数として受け取ります。送金元のアドレス取得
署名者オブジェクトからアドレスを取得します。
残高の確認
coin::balance
関数を使用して、送金元のAptosCoinにおける残高を確認します。条件付き資金転送
もし現在の残高が希望する残高より少ない場合、
aptos_account::transfer
関数を用いて不足分を宛先アドレスへ転送します。
このスクリプトは、Aptosブロックチェーンで資金の自動管理を行うための基本的な例を提供します。
例えば、支払いシステムや自動資金調達機能に応用可能です。
実行
Aptos CLIを使用してMoveスクリプトを実行する手順を詳しく説明します。
Moveスクリプトを効果的に実行するには、適切な環境設定、ファイルの配置、スクリプトのコンパイル、そして最終的にスクリプトの実行が必要です。
ステップバイステップガイド
1. ディレクトリの設定
新しい作業用ディレクトリを作成し、そのディレクトリに移動します。
mkdir testing
cd testing
2. Aptos CLIの設定とアカウントの作成
Aptos CLIを初期化して、新しいアカウントを設定します。
既存のプライベートキーを再利用するか、新しいキーを生成することができます。
aptos init --network devnet
3. Moveプロジェクトの初期化
新しいMoveプロジェクトを始めるために、以下のコマンドを実行します。
aptos move init --name run_script
4. スクリプトファイルの作成
sources/
サブディレクトリにmy_script.move
とmy_module.move
ファイルを作成します。
module addr::my_module {
public entry fun do_nothing() { }
}
5. ファイル構造
以下のようなファイル構造ができあがります。
testing/
Move.toml
sources/
my_script.move
my_module.move
6. スクリプトのコンパイル
コンパイルする時には--named-addresses
オプションを使用して、コード内で参照している名前付きアドレスを指定します。
aptos move compile --named-addresses addr=cb265645385819f3dbe71aac266e319e7f77aed252cacf2930b68102828bf615
7. コンパイル済みスクリプトの実行
コンパイルされたスクリプトを実行する時には、正しいパスと引数を指定します。
aptos move run-script --compiled-script-path build/my_script/bytecode_scripts/main.mv --args address:b078d693856a65401d492f99ca0d6a29a0c5c0e371bc2521570a86e40d95f823 --args u64:5
注意点
Move.toml
ファイル内で名前付きアドレスを定義することで、CLI引数を省略できます。
コンパイルされたスクリプトのパスはプロジェクト名に基づいています(この例ではrun_script
)。
このプロセスを通じて、Moveスクリプトがどのようにしてユーザー作成のMoveモジュールに依存し、Aptosブロックチェーン上で実行されるかを詳しく理解できます。
また、Rust SDKを使用する方法については、Aptos Developer Discussionsでさらに情報を得ることができます。
アドバンス
Aptos CLIを使用してMoveスクリプトをより効率的に実行する方法について述べています。
この方法では、コンパイルとスクリプト実行の二段階のプロセスを一つのコマンドで行うことができますが、一部の問題があるため、現在は従来の二段階のアプローチを推奨しています。
ストリームライン化されたスクリプト実行方法
一段階のコマンド実行
通常、Moveスクリプトを実行するにはまずaptos move compile
を使用してコンパイルし、次にaptos move run-script --compiled-script-path
を使用して実行します。
しかし、以下のコマンドを使用することで、これらのステップを一つの操作に統合できます。
aptos move run-script --script-path sources/my_script.move --args address:b078d693856a65401d492f99ca0d6a29a0c5c0e371bc2521570a86e40d95f823 --args u64:5
このコマンドは、スクリプトのパスと引数を指定して、スクリプトを直接コンパイルし、実行します。
これにより、作業の簡素化が図られ、プロセスが高速化されます。
注意点と推奨
問題点
この一段階のコマンドは便利ですが、場合によっては不具合や予期しない問題が発生する可能性があります。
推奨
現時点では、確実性と安定性を優先して、従来の二段階のプロセス(コンパイル後にスクリプトを実行)を使用することが推奨されます。
これにより、各ステップを個別に制御し、エラーの発生時に問題を特定しやすくなります。
この実行方法は、将来的にはMoveの開発プロセスをより効率的にする可能性がありますが、現在は慎重に扱い確実な方法を選択することが賢明です。
最後に
今回はAptosの「スクリプト」にまとめました。
これからも他のブロックチェーンやサービスについてもまとめていきます。
情報を見逃したくないという方はぜひ以下の購読ボタンを教えてください。
更新があった時、登録しているメールアドレス宛に通知が飛ぶようになります。
また、拡散もしてくれると嬉しいです🙇♂️