SuiとAptosはどちらもMove言語でスマートコントラクトを記述しますが、実際に開発を始めると多くの差異に気づきます。モジュールの記述方法、トランザクションの扱い方、テストの方法など、細部にわたって違いがあります。
本記事では、Move言語の基本的な構文から始め、SuiとAptosそれぞれの実装方式の違いをコード例を交えながら具体的に解説します。どちらのチェーンで開発を始めるべきか迷っている方や、両方を比較して理解を深めたい方に向けた内容となっています。
なお、Move言語は活発に開発が続いているため、本記事のコード例は執筆時点(2026年3月)の仕様に基づいています。最新情報は各公式ドキュメントを参照することを強くお勧めします。
Moveの基本構文——モジュールとリソースの定義
モジュールの宣言と基本構造
Moveのコードはモジュールという単位で管理されます。モジュールはアドレスと名前の組み合わせで識別されます。たとえば以下のようにモジュールを宣言します。
module my_address::my_module {
// リソースの定義と関数の実装
}
このアドレスの指定方法がSuiとAptosで異なります。Suiでは0xプレフィックスを持つ固有のオブジェクトIDを使用しますが、Aptosではデプロイ時にアカウントアドレスをそのままモジュールアドレスとして使用します。この違いはデプロイ戦略にも影響を与えます。
structとアビリティの宣言
Moveではデータ構造をstructで定義し、そのstructにアビリティ(ability)を付与することで振る舞いを制御します。主なアビリティはcopy(コピー可)、drop(破棄可)、store(グローバルストレージへの格納可)、key(グローバルストレージのキーとして使用可)の4種類です。
struct Coin has key, store {
value: u64,
}
この例では、Coinというリソースがkeyとstoreアビリティを持ちます。keyアビリティにより、このリソースはグローバルストレージに直接格納できます。copyアビリティを持たないため、複製は不可能です。これがMoveの安全性の核心です。
Sui Moveの特徴——オブジェクト指向の実装
オブジェクトの定義とUID
SuiのMoveでは、すべての状態が「オブジェクト」として管理されます。オブジェクトを定義するには、sui::object::UID型のフィールドを含む必要があります。
use sui::object::{Self, UID};
use sui::tx_context::TxContext;
struct MyNFT has key {
id: UID,
name: std::string::String,
}
UIDはSuiネットワーク上でグローバルに一意であることが保証されています。オブジェクトの生成にはobject::new(ctx)を使用し、コンテキスト(TxContext)から一意のIDが生成されます。この設計により、各オブジェクトをネットワーク全体で一意に追跡できます。
トランザクション関数とtransfer
Suiのトランザクション関数はentryキーワードで宣言します。関数内でオブジェクトを作成し、transfer::transferまたはtransfer::public_transferで所有者に送付します。
use sui::transfer;
public entry fun mint(ctx: &mut TxContext) {
let nft = MyNFT {
id: object::new(ctx),
name: std::string::utf8(b"My Token"),
};
transfer::transfer(nft, tx_context::sender(ctx));
}
Suiでは所有権の概念が明確で、オブジェクトは「所有されたオブジェクト」「共有オブジェクト」「不変オブジェクト」の3種類に分類されます。この分類がトランザクションの並列処理可否を決定します。
Aptos Moveの特徴——アカウントリソースによる管理
グローバルストレージへのリソース格納
AptosのMoveでは、リソースはアカウントアドレスをキーとしてグローバルストレージに格納されます。これはオリジナルのMoveの設計に近い方式です。
module my_addr::counter {
use std::signer;
struct Counter has key {
value: u64,
}
public fun initialize(account: &signer) {
move_to(account, Counter { value: 0 });
}
public fun increment(account: &signer) acquires Counter {
let counter = borrow_global_mut<Counter>(signer::address_of(account));
counter.value = counter.value + 1;
}
}
move_toでリソースをアカウントに格納し、borrow_global_mutで可変参照を取得します。acquiresキーワードにより、この関数がどのリソースにアクセスするかを明示します。この明示的な宣言がコードの可読性とセキュリティ審査のしやすさに寄与しています。
signerとEntryFunction
Aptosでは、トランザクションを送信するアカウントはsigner型として関数に渡されます。外部から呼び出し可能な関数はpublic entry funと宣言します。
public entry fun transfer_coin(
from: &signer,
to: address,
amount: u64
) acquires CoinStore {
// 転送ロジック
}
AptosのEntryFunction(エントリー関数)はAptosのトランザクションRPCから直接呼び出すことができます。この点ではSuiのentry関数と概念的に似ていますが、引数の型や渡し方に違いがあります。
開発環境のセットアップ——ローカル環境の構築手順
Suiのセットアップ手順
Suiの開発環境を構築するには、まずSui CLIをインストールします。macOSの場合はHomebrewを使用するか、公式サイトからバイナリをダウンロードする方法があります。
brew install sui
インストール後、sui move new project_nameで新しいプロジェクトを作成できます。プロジェクトにはMove.toml(依存関係の設定)とsources/ディレクトリが生成されます。sui move buildでコンパイル、sui move testでテストを実行します。ローカルテストネットの起動にはsui startコマンドを使用します。
Aptosのセットアップ手順
AptosはAptos CLIを使用して開発環境を構築します。
brew install aptos
新しいプロジェクトはaptos move init --name project_nameで作成します。こちらもMove.tomlとsourcesディレクトリが生成されます。aptos move compileでコンパイル、aptos move testでテストを実行します。デプロイはaptos move publishコマンドで行います。Suiとコマンド体系が似ているため、片方の経験があれば習熟は比較的スムーズです。
テスト方法の比較——単体テストと統合テスト
Suiのテスト機能
SuiのMoveでは、#[test]属性を付けた関数がテストとして認識されます。テスト専用のコンテキストを生成するにはsui::test_scenarioモジュールを使用します。
#[test]
fun test_mint() {
use sui::test_scenario;
let admin = @0xA;
let scenario = test_scenario::begin(admin);
// テストロジック
test_scenario::end(scenario);
}
test_scenarioモジュールにより、複数のトランザクションをシミュレートするシナリオテストが記述できます。これはDAppsのユーザーフローを模擬したエンドツーエンドテストに活用できます。
Aptosのテスト機能
AptosもMoveの標準的なテスト機能を使用します。#[test]属性に加え、#[expected_failure]でエラーケースのテストも記述できます。
#[test(account = @0x1)]
fun test_initialize(account: signer) {
initialize(&account);
assert!(exists<Counter>(@0x1), 0);
}
Aptosのテストでは、signerをテスト引数として受け取る形式が多く使われます。aptos move testコマンドでテストを実行でき、詳細なカバレッジレポートの出力も可能です。
デプロイとアップグレードの仕組み
Suiのパッケージ管理とアップグレード
Suiではスマートコントラクトをパッケージ(Package)という単位で管理します。パッケージは一度デプロイされると不変ですが、アップグレード可能なパッケージを作成する仕組みも提供されています。アップグレードにはUpgradeCap(アップグレード権限オブジェクト)が必要で、この権限管理がセキュリティの要となります。
パッケージのデプロイコマンドはsui client publish --gas-budget 100000000です。ガス代の指定が必要な点に注意が必要です。
Aptosのモジュールアップグレード
Aptosでは、同じアドレスに新しいバージョンのモジュールを上書きする形でアップグレードを行います。アップグレードポリシーには「互換性あり」「変更不可」の設定があり、デプロイ時にポリシーを選択します。互換性ポリシーでは後方互換性のある変更のみが許可されます。
デプロイコマンドはaptos move publish --named-addresses my_addr=0xYOUR_ADDRESSです。named-addressesによりコード内のアドレスを実際のアカウントアドレスに置き換えます。
まとめ
SuiとAptosのMove開発を比較すると、オブジェクト指向のSuiと、アカウントリソース管理のAptosという明確な違いが見えてきます。Suiはオブジェクト操作が多いNFTやゲーム系DAppsに適しており、AptosはDeFiや金融系の用途で強みを発揮する傾向があります。
開発ツールの完成度や開発者体験はどちらも高水準です。すでに他の言語で開発経験がある方は、公式チュートリアルを通じて比較的スムーズに習熟できるでしょう。どちらを選ぶかは、開発するDAppsのユースケースと、コミュニティの活発さなどを総合的に判断することをお勧めします。
よくある質問
Move言語はRustに似ていますか?
Move言語はRustから多くのインスピレーションを受けており、所有権の概念や型安全性に共通点があります。Rustの経験がある方はMove入門がスムーズに進む傾向があります。ただし、Move特有のリソース型やアビリティシステムはMove独自の概念であるため、別途学習が必要です。
SuiとAptosのガス代はどちらが安いですか?
ガス代は市場状況やネットワークの混雑状況によって変動します。一般的にSuiとAptosはどちらも低いガス代を実現するよう設計されていますが、具体的な比較は時期や操作の種類によって異なります。最新のガス代情報は各チェーンのエクスプローラーで確認してください。
Move言語で書いたコードをSuiとAptos両方にデプロイできますか?
基本的には難しいと考えてください。SuiとAptosはMove言語を採用しているものの、それぞれ独自の拡張や差異があります。特にSuiのオブジェクト指向設計はAptos MVMとは根本的に異なるため、コードの移植には大幅な書き直しが必要になります。共通部分(純粋な計算ロジックなど)は再利用できる場合もありますが、チェーン固有の機能に依存する部分は個別に実装する必要があります。
※本記事は情報提供を目的としており、投資を推奨するものではありません。暗号資産への投資は元本割れのリスクがあります。投資判断はご自身の責任で行ってください。