sui-publish について
sui-publishは、Sui Moveパッケージの公開・アップグレード、ネットワーク利用、デプロイ準備を扱うリファレンススキルです。Sui CLIを使う開発者向けに、一時的なテスト環境やローカルネットワーク、公開前チェックを案内します。
主な機能
- 01Moveパッケージの公開とアップグレード
- 02一時的なネットワークでの公開テスト
- 03アップグレードポリシーの設定
- 04ネットワークの切り替え
- 05ローカルネットワークでの開発手順
- 06Mainnet公開前のチェック
活用シーン
- 01デプロイ前にパッケージ初期化をテスト
- 02複数のネットワークへのパッケージ公開
- 03コード変更前にアップグレード制約を確認
- 04ローカルのSuiネットワークで開発・反復
MCP 対応
利用可能な場合、最新情報の確認やリファレンスにない内容の調査に、SuiドキュメントMCPサーバー https://sui.mcp.kapa.ai を参照するよう案内しています。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mystenlabs/ skills --skill sui-publishインストール後、Claude Code で/sui-publishを入力して読み込まれたか確認します。
同梱ファイルevals/evals.json
詳細
パッケージの公開、デプロイ、ローカルネットワーク
MCP ツール: お使いの環境で利用できる場合は、最新の回答を得るために Sui ドキュメントの MCP サーバー(
https://sui.mcp.kapa.ai)にも問い合わせてください。情報の検証や、これらのリファレンスファイルに記載されていない詳細の確認に使用してください。
情報源の制約: 情報源は docs.sui.io と MystenLabs/sui-stack-hello-world のみに限定してください。
パッケージの公開
hello-world の標準的な公開手順
フルスタックのスターターでは、既存の hello-world パッケージのみを公開してください。
cd sui-stack-hello-world/move/hello-world
sui move build
sui client publish
公開時の出力に含まれるパッケージ ID を使って、sui-stack-hello-world/ui/src/constants.ts の TESTNET_HELLO_WORLD_PACKAGE_ID を更新してください。別のカウンターパッケージを公開したり、2 つ目のプロジェクトディレクトリを作成したりしないでください。
公開前のチェックリスト
- アクティブな環境を確認します:
sui client active-env - SUI トークンを保有していることを確認します:
sui client balance - ビルドが正常に完了することを確認します:
sui move build
公開
sui client publish
パッケージがアクティブなネットワークにデプロイされ、次の情報が返されます。
- 一意のパッケージ ID(以後のすべての操作で使用します)
- UpgradeCap オブジェクト(お客様のアドレスに送信され、今後のアップグレードを制御します)
init関数で作成されたオブジェクトの ID
テスト公開(エフェメラルネットワーク)
sui client test-publish を使うと、実際のネットワークに状態を永続化せずに、テスト用のエフェメラル環境へパッケージを公開できます。
sui client test-publish
このコマンドはパッケージを公開して init 関数を実行し、sui client publish と同じ出力(パッケージ ID、UpgradeCap、作成されたオブジェクト)を返しますが、デプロイは永続化されません。次の用途に使用してください。
- 実際に公開する前に、
init関数が正しく実行されることを確認する - Testnet/Devnet のリソースを消費せずに、CI で公開とアップグレードのフローをテストする
- Mainnet にデプロイする前に、ガスコストとオブジェクトの作成を検証する
test-publish は複数環境のパッケージで --build-env をサポートします。
sui client test-publish --build-env testnet
公開後
公開トランザクションの出力では、パッケージ ID が作成されたオブジェクトのセクション(UpgradeCap や init 関数で作成されたオブジェクトと並んでいる箇所)に表示されます。また、published-at フィールドが Published.toml に自動的に追加されます。公開したパッケージを操作するには、次のようにします。
# Call a function
sui client call --package <PACKAGE_ID> --module greeting --function new
# Query an object
sui client object <OBJECT_ID>
「Your package is already published」エラー
sui client publish の実行時にこのエラーが表示された場合、アクティブな環境のエントリがすでに Published.toml にあることを意味します。これは、開発中にパッケージを繰り返し調整しているときに発生します。
- 別のネットワークに公開するには、
sui client switch --env <ENV>で環境を切り替えてから、再度sui client publishを実行します。ツールチェーンは環境ごとに公開済みアドレスをPublished.tomlで自動的に管理するため、Published.tomlを削除しないでください。 - 同じネットワーク上の既存パッケージをアップグレードするには、
sui client upgradeを使用します(下記を参照)。
公開済みパッケージのアップグレード
公開済みパッケージは不変ですが、元のパッケージに関連付けた新しいバージョンを公開することでアップグレードできます。アップグレード権限は UpgradeCap オブジェクトによって制御されます。重要: publish コマンドと同じ PTB 内で UpgradeCap を制限できます。たとえば、パッケージを公開し、同じアトミックなトランザクション内ですぐに sui::package::only_additive_upgrades を呼び出すことができます。また、パッケージを恒久的に不変にするため、UpgradeCap を完全に破棄することもできます(下記のアップグレードポリシーを参照)。
sui client upgrade --upgrade-capability <CAP_ID>
UpgradeCap の確認方法
UpgradeCap オブジェクト ID は、アップグレードのたびに必要です。確認方法はいくつかあります。
- Published.toml(推奨):公開後、ツールチェーンは各環境の
Published.tomlにあるupgrade-capabilityフィールドに cap ID を記録します。 - 所有オブジェクトを照会する:公開アドレスが所有するすべての
UpgradeCapオブジェクトを一覧表示します。sui client objects --type 0x2::package::UpgradeCap - 公開トランザクションの出力:元の
sui client publishの出力には、作成されたオブジェクトの一覧にUpgradeCapオブジェクト ID が含まれています。 - エクスプローラー:SuiVision(
suivision.xyz)または Suiscan(suiscan.xyz)でアドレスを検索し、所有オブジェクトを型0x2::package::UpgradeCapで絞り込みます。
アップグレードポリシー
アップグレードポリシーによって、変更可能な内容が制限されます。
- Compatible(デフォルト):最も制限の緩いポリシーです。詳しいルールは以下を参照してください。
- **Additive:**新しいモジュールを追加できますが、既存のモジュールは一切変更できません。
- **Dependency-only:**依存関係のバージョンのみ更新できます。コードは変更できません。
公開と同じ PTB 内で UpgradeCap を制限する:publish コマンドと同じプログラマブルトランザクションブロック内で UpgradeCap を制限できます。たとえば、公開直後に同一のアトミックトランザクション内で UpgradeCap に対して sui::package::only_additive_upgrades を呼び出します。これは、最初からアップグレードポリシーを固定するための推奨方法です。一度制限すると、ポリシーを緩和することはできません。
その他の UpgradeCap の選択肢:
- マルチシグアドレスに転送することで、アップグレードのガバナンスを共有できます。
- UpgradeCap を破棄することで、パッケージを恒久的に不変にできます。
sui::package::make_immutableを呼び出すと、UpgradeCapオブジェクトが消費され、破棄されます。cap が破棄されると、誰もパッケージをアップグレードできなくなります。この操作は取り消せません。
Compatible アップグレードのルール(詳細)
compatible ポリシーでは、次の変更が許可されます。
- 新しい関数を追加する(public または private)
- 新しいモジュールを追加する
- 関数の実装(本体)を変更する
- 新しい構造体型を追加する
- private/friend 関数のシグネチャを変更する
次の変更は互換性を損なうため、拒否されます。
- 既存のモジュールを削除または名前変更する
- public 関数を削除または名前変更する
- public 関数のシグネチャ(パラメーター、戻り値の型、型パラメーター)を変更する
- 構造体のフィールドを削除、名前変更、または並べ替える
- 構造体のフィールドの型を変更する
- 構造体の能力(
key、store、copy、drop)を追加または削除する - 構造体型を完全に削除する
- 構造体の型パラメーターを変更する
アップグレードの前に、これらのルールに照らして差分を確認してください。sui client upgrade コマンドは、互換性のない変更をビルド時に説明的なエラーとともに拒否します。
アップグレード後の型の固定
**構造体型は、最初に公開された元のパッケージ ID に恒久的に固定されます。**アップグレード後、新しいパッケージには新しい ID が割り当てられますが、アップグレードされたコードで作成されるすべてのオブジェクトの型は、引き続き元のパッケージ ID をルートとします。
これは次のような重大な影響をもたらします。
- 型によるオブジェクトの照会(たとえば、
typeフィルターを指定したlistOwnedObjects)では、元のパッケージ ID を使用する必要があります。 moveCallによる関数の呼び出しでは、アップグレード後の(最新の)パッケージ ID を使用する必要があります。- フロントエンドアプリでは、両方の ID を保持してください。型の照会には
ORIGINAL_PACKAGE_ID、関数の呼び出しにはPACKAGE_IDを使用します。
// Original publish → package ID 0x1234...
// After upgrade → package ID 0x5678...
// Query: use ORIGINAL package ID
client.core.listOwnedObjects({
owner: addr,
type: '0x1234...::module::MyObject', // ✅ original ID
});
// Call: use UPGRADED package ID
tx.moveCall({
target: '0x5678...::module::my_function', // ✅ upgraded ID
});
複数のネットワークへの公開
別のネットワークに公開するには(たとえば、Testnet から Devnet に切り替える場合)、環境を切り替えて再度公開します。パッケージにはネットワークごとに異なる ID が割り当てられます。Published.toml ファイルには、環境ごとの公開済みアドレスが記録されます。
新しいネットワークに公開する前に、そのネットワークのトークンを用意してください。
- Testnet: Web フォーセット
faucet.sui.io、Discord(#testnet-faucetで!faucet <ADDRESS>)、または TypeScript SDK(requestSuiFromFaucetV2())から無料トークンを取得できます。sui client faucetは Testnet では機能しません。 - Devnet:
sui client faucet、Web フォーセットfaucet.sui.io、Discord(#devnet-faucetで!faucet <ADDRESS>)、または TypeScript SDK から無料トークンを取得できます。 - Localnet:
sui client faucet、またはローカルフォーセット127.0.0.1:5003/gasもしくは127.0.0.1:9123/gas(sui start --with-faucet --force-regenesisで起動)から無料トークンを取得できます。 - Mainnet: 実際の金銭的価値を持つ SUI トークンが必要です。取引所または送金で入手してください。フォーセットはありません。
外部署名用のシリアライズ
別の当事者(たとえばマルチシグ)による署名用にトランザクションのバイト列を生成するには、次のようにします。
sui client publish --serialize-output
実行の代わりに、base64 形式のトランザクションバイト列が出力されます。
ローカルネットワーク(localnet)
Localnet は、お使いのマシン上で完全な Sui ネットワークを実行し、オフライン開発と迅速な反復を可能にします。次のコマンドで起動します。
sui start --with-faucet --force-regenesis
--force-regenesis フラグは、ネットワークの起動ごとにオンチェーンの状態をすべてリセットするため、再起動のたびにクリーンな環境が得られます。--with-faucet フラグは、アドレスにトークンを付与できるローカルフォーセットを起動します。
CLI を localnet に接続するには、次のようにします。
sui client switch --env localnet
ローカルトークンは sui client faucet を使うか、ローカルフォーセットのエンドポイント 127.0.0.1:5003/gas または 127.0.0.1:9123/gas に直接アクセスして取得できます。
Localnet は次の用途に便利です。
- Testnet/Devnet の稼働状況に依存しないオフライン開発
- 公開やアップグレードのワークフローを迅速に反復すること(再起動のたびに状態をリセット)
- 共有ネットワークにデプロイする前の
init関数とオブジェクト作成のテスト
Mainnet 公開チェックリスト
Mainnet へのパッケージ公開を準備する際は、このチェックリストを使用してください。公開トランザクションを実行する前に、すべての項目を確認してください。
1. テストとカバレッジ
テストスイート全体を実行し、すべてのテストが成功することを確認します。
sui move test
カバレッジのしきい値がプロジェクトで必要な場合は、カバレッジをレポートします。
sui move test --coverage
sui move coverage summary
先に進む前に、失敗したテストを修正してください。テストしていないコードを Mainnet に公開しないでください。
2. 依存関係とアドレス
Move.tomlでedition = "2024"が使用され、レガシーな[addresses]セクションや Git ベースの Sui フレームワーク依存関係がないことを確認します。[environments]に正しいチェーン ID を持つmainnetエントリが含まれていることを確認します。- MVR 依存関係(
{ r.mvr = "@org/package" })を使用している場合は、Mainnet 上で解決できることを確認します。 sui move buildを実行し、警告なしでコンパイルできることを確認します。
3. アップグレードポリシーの決定
公開する前にアップグレードポリシーを決定してください。後からポリシーを拡張することはできません。
| ポリシー | 変更できる内容 | 使用するタイミング |
|---|---|---|
| Compatible(デフォルト) | 関数の追加、モジュールの追加、実装の更新。関数の削除や構造体レイアウトの変更はできません。 | ほとんどのパッケージに適しています。型安全性を保ちながら、バグ修正の柔軟性を確保できます。 |
| Additive | 新しいモジュールの追加のみ。既存のモジュールは凍結されます。 | 機能を拡張しつつ、既存のコードが変更されないことを保証したいパッケージに使用します。 |
| Dependency-only | 依存関係のバージョン更新のみ。 | ほぼ最終段階にあり、フレームワークの更新のみを追跡するパッケージに使用します。 |
| Immutable | 変更できません。パッケージは永久に凍結されます。 | 不変性が信頼性の保証となる、完全に監査済みのパッケージ(例:トークンコントラクト)に使用します。 |
公開 PTB 内の UpgradeCap に対して、sui::package::only_additive_upgrades、only_dep_upgrades、または make_immutable への moveCall を含めると、公開と同じトランザクション内でポリシーを制限できます。
4. ガスの見積もり
Mainnet の SUI には実際の金銭的価値があります。公開する前にガスを見積もってください。
sui client publish --dry-run
ドライランの出力には、computationCost、storageCost、storageRebate が含まれます。必要なガスの合計は computationCost + storageCost - storageRebate です。この金額に余裕分を加えた額をアドレスが保有していることを確認してください。
5. 署名者とカストディの計画
公開アドレスと UpgradeCap を誰が管理するか決めます。
- 単一署名者: 最もシンプルです。1つのキーで公開し、
UpgradeCapを保持します。個人プロジェクトや開発初期段階に適しています。 - マルチシグ: チームや価値の高いパッケージ向けです。マルチシグアドレスを作成し、
--serialize-outputを使って公開してから、必要な署名者がオフラインで署名します。公開と同じ PTB 内でUpgradeCapをマルチシグアドレスに移転します。 - 公開時にイミュータブルにする: 今後アップグレードが不要な場合は、公開用 PTB で
UpgradeCapを破棄します(sui::package::make_immutable)。これにより、カストディに関する懸念を完全になくせます。
マルチシグで公開する場合:
# Generate unsigned transaction bytes
sui client publish --serialize-output
# Each signer signs the bytes, then combine and execute
6. 公開前の最終確認
Mainnet で公開トランザクションを実行する前に、以下を確認してください。
-
sui client active-envの結果がmainnet -
sui client balanceでガスに十分な SUI があることを確認(ドライランの見積もりを確認) -
sui move buildが警告なしで成功する -
sui move testがすべてのテストに合格する -
Move.tomlのeditionが正しく、レガシー形式が使われていない - アップグレードポリシーが決定済みで、該当する場合は制限の呼び出しが PTB に含まれている
- 署名者のキーまたはマルチシグの準備ができている
- 先に Testnet でパッケージを検証済みである(同じコード、同じテスト、同じ公開フロー)
ドライランとトランザクションのデバッグ
ドライランは、ネットワークに送信せずにトランザクションをシミュレーションします。ドライランは次の目的で使用します。
- 実行前にガスコストを見積もる。
- ユーザーに署名を依頼する前に、トランザクションが成功することを確認する。
- ガスを消費する前にエラーを確認し、失敗するトランザクションをデバッグする。
Slush などのウォレットは、署名用のトランザクションを提示する前に自動でドライランを実行します。ドライランに失敗すると、ウォレットは署名を促す代わりにエラーを表示します。
TypeScript SDK からは、devInspectTransactionBlock を使ってトランザクションをプログラムでドライランできます。CLI からは、--dry-run フラグで実行をシミュレーションできます。
ドライランの失敗をデバッグするときは、すべてのオブジェクト ID が正しいこと、オブジェクトのバージョンが最新であること、送信者が十分なガスを保有していること、関数の引数が期待される型と一致すること、アクティブな環境(sui client active-env)がパッケージを公開したネットワークと一致することを確認してください。
本番環境のモニタリング
Sui パッケージは公開後にイミュータブルになるため、モニタリングが重要です。稼働中のコントラクトにホットフィックスを適用することはできず、アップグレードを公開する必要があります。
モニタリング対象
| シグナル | 方法 | 理由 |
|---|---|---|
| パッケージに関連する失敗したトランザクション | gRPC ストリーミング経由でトランザクションエフェクトを購読し、パッケージ ID でフィルタリング | 本番環境での Move のアボート、ガス不足、予期しないリバートを検出します |
| ガス消費量 | トランザクションエフェクトから gasUsed を追跡 | 想定外にコストの高い操作や、ガス枯渇攻撃を検出します |
| イベントの発行 | gRPC ストリーミング経由で、型({packageId}::module::EventName)ごとにイベントを購読 | ミント、転送、管理者操作、拒否リストの変更など、ビジネスの中核となるテレメトリーを取得します |
| オブジェクトの作成/削除率 | 自身の型でフィルタリングして、オブジェクトの変更をクエリまたは購読 | 異常なアクティビティ(大量ミント、オブジェクトのスパム)を検出します |
| 管理者操作/ケイパビリティの使用状況 | ケイパビリティで制御される操作のイベントをフィルタリング | 不正な管理操作や予期しない管理操作を検出します |
| 共有オブジェクトの競合 | 共有オブジェクトを使うトランザクションのレイテンシーを監視 | 競合が多いと UX が低下します。オブジェクトのシャーディングが必要になる場合があります |
実装
リアルタイム監視には、gRPC ストリーミングサブスクリプションを使用します。
for await (const event of client.subscriptionService.subscribeEvents({
filter: { MoveEventModule: { package: PACKAGE_ID, module: 'my_module' } },
})) {
// Forward to your monitoring stack (Grafana, Datadog, PagerDuty, etc.)
}
履歴分析には、カスタムインデクサー(sui-indexer-alt)を実行して、関連するイベントとトランザクションの効果を独自のデータベースに書き込みます。accessing-data スキルの indexers.md を参照してください。
Move コード内のセキュリティ上重要な操作(管理者の変更、設定の更新、拒否リストの変更、オブジェクトの削除)ごとにイベントを発行してください。オフチェーンシステムがこうした操作を認識する唯一の方法は、イベントです。
ロールバックとインシデント対応
Sui パッケージはロールバックできません。 公開済みのバイトコードは不変です。revert や rollback コマンドはありません。復旧するには、修正を適用したアップグレードを公開する必要があります。
不適切なアップグレードを公開した場合
- 影響範囲を評価します。 影響を受ける関数を特定します。以前のバージョンで作成された既存のオブジェクトは引き続き有効です。これらの型は元のパッケージ ID に固定されています。
- ただちに修正アップグレードを公開します。 修正したコードを記述し、テストを実行して、Testnet でドライランを行った後、Mainnet で
sui client upgradeを実行します。以後のすべての呼び出しでは、新しいパッケージ ID が古いものに代わって使用されます。 - フロントエンドを更新します。
PACKAGE_IDSを新しい(修正済みの)パッケージ ID に更新します。型のクエリでは引き続きORIGINAL_PACKAGE_IDSを使用します。 - 告知します。 バグがユーザー向けの動作に影響した場合は、アプリのチャネルを通じてユーザーに通知します。
UpgradeCap が侵害された場合
UpgradeCap を持つ攻撃者は、あなたのパッケージで任意のコードを公開できます。対策:
- まだ自分が cap を保持している場合: さらなる悪意あるアップグレードを防ぐため、ただちに制限します(
only_dep_upgradesまたはmake_immutable)。 - 攻撃者が cap を保持している場合: アップグレード権限を取り戻すことはできません。新しいパッケージを公開し、ユーザーを移行させて、その移行について告知します。本番パッケージで
UpgradeCapの管理にマルチシグが重要である理由はここにあります。
共有オブジェクトが破損した場合
不具合のある関数が共有オブジェクトに無効な状態を書き込むことがあります。共有オブジェクトはどのトランザクションでも変更できるため:
- アップグレードできる場合: 破損した状態を修復する修復関数を含むアップグレードを公開します。
AdminCapによって実行を制限します。 - パッケージが不変の場合: 唯一の選択肢は、旧オブジェクトのデータを読み取り(アクセス可能な場合)、修正済みのオブジェクトを作成する移行関数を含む新しいパッケージをデプロイすることです。
予防チェックリスト
-
UpgradeCapをマルチシグで管理するか、additive/dep_onlyに制限する - 同じコードと同じ公開フローで、すべてのアップグレードを Testnet でテストする
- 監視用に、管理者の操作でイベントを発行する
- 重要な共有オブジェクトには、capability によって実行を制限した修復関数を用意する
- フロントエンドを再デプロイせずにパッケージ ID を切り替えられるようにする(ハードコードではなく環境設定を使用)