walrus-ts-sdk について
walrus-ts-sdkは、公式の`@mysten/walrus` TypeScript SDKでWalrusのBlobやファイルを読み書きするためのスキルです。ストレージノードへの直接アクセス、アップロードリレー、再開可能な段階的書き込みを扱います。
主な機能
- 01Blobとファイルの読み書き
- 02Quilt内のファイル処理
- 03アップロードリレーによるブラウザー送信
- 04中断した書き込みフローの再開
- 05SuiおよびWalrusクライアントの設定
- 06利用前のBlob可用性確認
活用シーン
- 01TypeScriptアプリへのWalrusストレージ導入
- 02ブラウザーウォレットでのアップロード
- 03中断したBlobアップロードの再開
- 04保存データに依存する前の認証状態確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ mystenlabs/ walrus-skills --skill walrus-ts-sdkインストール後、Claude Code で/walrus-ts-sdkを入力して読み込まれたか確認します。
同梱ファイルevals/evals.json
詳細
Walrus TypeScript SDK
ソースの制約: このスキルの情報は、 Walrus TypeScript SDK のドキュメント、 公式サンプル、 およびWalrus のドキュメントからのみ取得しています。 このスキルを拡張する場合は、これらのソースのみを参照してください。
@mysten/walrus パッケージは、Mysten Labs が保守する Walrus の公式 TypeScript SDK です。Walrus のストレージノードに直接接続するか、アップロードリレー経由で動作します。リレーを使わない場合、ブロブの書き込みには約 2,200 件、読み込みには約 335 件のリクエストが必要です。そのため、多くのアプリケーションでは、SDK のドキュメントはパブリッシャーとアグリゲーターの利用を推奨しています。この SDK は、Walrus と直接やり取りする必要があるアプリケーションや、ユーザーが自分でストレージ料金を支払うアプリケーション向けです。
Walrus に保存されるすべてのブロブは公開されています。機密情報を保存する場合は、保存前にクライアント側で暗号化してください(walrus-data-security を参照)。
このスキルのすべてのパターンは、以下を参考にしています。
- https://sdk.mystenlabs.com/walrus
- https://github.com/MystenLabs/ts-sdks/tree/main/packages/walrus/examples
- https://docs.wal.app/docs/walrus-client/verifying-availability
SDK API に不明な点がある場合は、回答する前に該当するページを取得してください。メソッドの一覧は、WalrusClient TypeDocsで確認できます。
関連スキル
| トピック | スキル | 読み込むタイミング |
|---|---|---|
| CLI 操作 | walrus-cli | SDK ではなく walrus バイナリを使う場合 |
| HTTP API | walrus-http-api | 任意の言語から REST エンドポイントを使う場合 |
| 機密性 | walrus-data-security | アップロード前にブロブを暗号化する場合 |
| ブロブ管理 | walrus-blob-lifecycle | ブロブの延長、削除、共有、または可用性の検証を行う場合 |
| Quilts | walrus-quilts | 多数の小さなブロブを 1 つの単位にまとめる場合 |
| Move 連携 | walrus-move-integration | Walrus ブロブを Move コントラクトでラップする場合 |
スキルの内容
主な概念
- クライアントのセットアップ。 Sui クライアントを作成し、Walrus 拡張機能で拡張します:
new SuiGrpcClient({ network, baseUrl }).$extend(walrus())。拡張機能にはclient.walrusからアクセスします。新しいコードでは、非推奨の JSON-RPCSuiClientを使用しないでください。 - 2 つの API レベル。
WalrusFileAPI(getFiles、writeFiles)は、通常のブロブおよび Quilts として保存されたデータを扱うもので、推奨されるエントリーポイントです。低レベルのreadBlob/writeBlobAPI は、単一のUint8Arrayブロブを扱います。 - 処理フロー。
writeFilesFlowとwriteBlobFlowは、書き込みをencode、register、upload、certifyの各ステップに分割します。ブラウザーウォレット(署名ごとにユーザー操作が必要)や、クラッシュから復旧可能なアップロード(run()は保存可能なステップを返し、それをresumeとして渡して再開)には、これらを使用してください。 - 署名者。 書き込みには、登録と認証のトランザクション用の SUI と、ストレージ料金および書き込み手数料用の WAL を保有するアドレスの
Signerが必要です。読み込みには署名者は必要ありません。 - アップロードリレー。 ブラウザーやモバイルのクライアントでは、すべてのシャードに到達するのに十分な数の接続を開けません。
uploadRelayオプションを使うと、ブロブをリレーに送信してスライスを配布できます。クライアントは引き続き Sui 上で署名し、支払いを行います。リレーによってはチップが必要です。 - ブロブ ID とオブジェクト ID。
blobId(URL セーフな base64。コンテンツから導出されます)は読み込みに使用します。blobObject.id(0x...)は、ライフサイクル操作および可用性の検証に使う Sui オブジェクトです。 - コミュニティ SDK。 TypeScript 以外の言語向けには、Go、PHP、Python のコミュニティ SDK があります。これらは HTTP 経由でアグリゲーターやパブリッシャーと通信します。
インストールとセットアップ
npm install --save @mysten/walrus @mysten/sui
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { walrus } from '@mysten/walrus';
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(walrus());
SDK には、各ネットワークのパッケージ ID とオブジェクト ID が含まれています。別のデプロイ先を指定するには、walrus({...}) に packageConfig: { systemObjectId, stakingPoolId } を渡します。ストレージノードへのリクエスト(タイムアウト、再試行、ログ記録)をカスタマイズするには、storageNodeClientOptions: { fetch, timeout, onError } を渡します。
WalrusFile API を使った読み取り
getFiles は blob ID と quilt ID を受け取り、WalrusFile オブジェクトを返します。クライアントが 1 つの quilt から複数のファイルを効率よく読み込めるよう、バッチで読み取ります。
const [file1, file2] = await client.walrus.getFiles({ ids: [anyBlobId, orQuiltId] });
const bytes = await file1.bytes(); // Uint8Array
const text = await file1.text(); // utf-8 string
const json = await file2.json(); // parsed JSON
// Files stored in a quilt also carry an identifier and tags
const identifier: string | null = await file1.getIdentifier();
const tags: Record<string, string> = await file1.getTags();
blob ID を使って WalrusBlob を取得し、quilt の場合は識別子、タグ、または quilt ID でファイルを一覧表示することもできます。
const blob = await client.walrus.getBlob({ blobId });
const files = await blob.files();
const [readme] = await blob.files({ identifiers: ['README.md'] });
const textFiles = await blob.files({ tags: [{ 'content-type': 'text/plain' }] });
WalrusFile API を使った書き込み
Uint8Array、Blob、または文字列からファイルを作成して書き込みます。1 回の writeFiles 呼び出しで書き込むファイルはすべて 1 つの quilt に格納されます。そのため、可能であれば複数のファイルをまとめて書き込みます。quilt のエンコードは、ファイルが 1 つだけの場合は効率が低くなります。
import { WalrusFile } from '@mysten/walrus';
const file1 = WalrusFile.from({ contents: new Uint8Array([1, 2, 3]), identifier: 'file1.bin' });
const file2 = WalrusFile.from({
contents: new TextEncoder().encode('Hello from the TS SDK!!!\n'),
identifier: 'README.md',
tags: { 'content-type': 'text/plain' },
});
const results = await client.walrus.writeFiles({
files: [file1, file2],
epochs: 3,
deletable: true,
signer: keypair,
});
// results: { id, blobId, blobObject }[]
生の blob:readBlob と writeBlob
const data = await client.walrus.readBlob({ blobId }); // Uint8Array
const { blobId } = await client.walrus.writeBlob({
blob: new TextEncoder().encode('Hello from the TS SDK!!!\n'),
deletable: false,
epochs: 3,
signer: keypair,
});
writeBlob と writeFiles は、クラッシュから復旧できるアップロードのために onStep と resume を受け付けます。onStep から各ステップを保存し、クラッシュ後に続行するには、保存したステップを resume として渡します。
フロー:ブラウザーウォレットと再開可能なアップロード
ブラウザーウォレットは署名のためにポップアップを開きます。ユーザー操作への直接の応答として開かれなかったポップアップは、ブロックされることがあります。writeFilesFlow は書き込みを分割するため、登録トランザクションと認証トランザクションをそれぞれ別のボタンから署名できます。
const flow = client.walrus.writeFilesFlow({
files: [WalrusFile.from({ contents: new Uint8Array(fileData), identifier: 'my-file.txt' })],
});
await flow.encode();
// Button 1
async function handleRegister() {
const registerTx = flow.register({ epochs: 3, owner: currentAccount.address, deletable: true });
const result = await signAndExecuteTransaction({ transaction: registerTx });
if (result.$kind === 'FailedTransaction') {
throw new Error(`Registration failed: ${result.FailedTransaction.status.error?.message}`);
}
await flow.upload({ digest: result.Transaction.digest });
}
// Button 2
async function handleCertify() {
const certifyTx = flow.certify();
const result = await signAndExecuteTransaction({ transaction: certifyTx });
if (result.$kind === 'FailedTransaction') {
throw new Error(`Certification failed: ${result.FailedTransaction.status.error?.message}`);
}
const files = await flow.listFiles();
}
個別の操作を行わずに実行する場合は、パイプライン全体を非同期イテレーターとして実行し、各ステップを保存します。
const flow = client.walrus.writeBlobFlow({ blob });
for await (const step of flow.run({ signer, epochs: 3, deletable: true })) {
await db.save(fileId, step); // a WriteBlobStep, persisted for crash recovery
}
// Resume: completed steps are skipped and only missing slivers are uploaded
const resumed = client.walrus.writeBlobFlow({ blob, resume: await db.load(fileId) });
executeRegister({ signer, epochs, deletable, owner }) と executeCertify({ signer }) は、署名を代行し、型付きのステップ結果を返します。
ブラウザーアプリ向けアップロードリレー
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(
walrus({
uploadRelay: {
host: 'https://upload-relay.testnet.walrus.space',
sendTip: { max: 1_000 }, // maximum tip in MIST; the client reads the required tip itself
},
}),
);
hostは必須です。Mainnet:https://upload-relay.mainnet.walrus.space。Testnet:https://upload-relay.testnet.walrus.space。- リレーは
<host>/v1/tip-configでチップの設定を公開します。チップはconst(blob ごとに固定)またはlinear(baseとperEncodedKib)です。sendTip: { max }を指定すると、クライアントは最大額まで支払います。チップを手動で設定するには、sendTip: { address, kind: { const: 105 } }またはkind: { linear: { base: 105, perEncodedKib: 10 } }を渡します。 - 必要なチップが
maxを超えると、アップロードはTip amount exceeds the maximum allowed tipというエラーで失敗します。maxを引き上げてください。
blob に依存する前に検証する
書き込みの結果だけでは、blob が永続的に保存されている証拠にはなりません。Blob オブジェクトを読み戻し、認証済みで、ストレージ期間内にあり、削除可能ではないことを確認します。
const blob = await walrusClient.getBlobObject(blobObjectId); // blobObject.id from the write result
const durable =
blob.certified_epoch != null && // null until certified
currentEpoch < blob.storage.end_epoch && // read the epoch from system state or `walrus info`
!blob.deletable;
if (!durable) throw new Error('Blob is not durably available yet; do not depend on it');
Sui オブジェクトの blob_id フィールドは u256 であり、アグリゲーターの URL で使われる base64 blob ID ではありません。このフィールドを URL に直接組み込まず、オブジェクト ID で読み取ってください。可用性と整合性の完全な確認方法については、walrus-blob-lifecycle を参照してください。
エラー処理
エポックの切り替わり前後に古いキャッシュデータから発生するエラーは、RetryableWalrusClientError を拡張します。クライアントをリセットして再試行してください。
import { RetryableWalrusClientError } from '@mysten/walrus';
if (error instanceof RetryableWalrusClientError) {
client.walrus.reset();
// retry the operation
}
readBlob などの高レベルメソッドは、これらのケースをすでに再試行し、一部のノードが失敗しても処理を続行します。SDK が吸収するストレージノード個別のエラーを確認するには、storageNodeClientOptions: { onError: (error) => console.log(error) } を渡します。
WASM の読み込み:Vite と Next.js
SDK では blob のエンコードとデコードに WASM バインディングが必要です。Node.js、Bun、および一部のバンドラーでは設定は不要です。バンドラーがバイナリを見つけられない場合は、クライアントにその場所を指定します。
// Vite
import walrusWasmUrl from '@mysten/walrus-wasm/web/walrus_wasm_bg.wasm?url';
const client = new SuiGrpcClient({ network: 'testnet', baseUrl: 'https://fullnode.testnet.sui.io:443' })
.$extend(walrus({ wasmUrl: walrusWasmUrl }));
// Or load from a CDN / self-host
walrus({ wasmUrl: 'https://unpkg.com/@mysten/walrus-wasm@latest/web/walrus_wasm_bg.wasm' });
// Next.js API routes (webpack and Turbopack alike)
const nextConfig: NextConfig = {
serverExternalPackages: ['@mysten/walrus', '@mysten/walrus-wasm'],
};
serverExternalPackages を指定せずに WASM ローダーをサーバールートにバンドルすると、バイナリの場所を特定できなくなることがあります。Turbopack では、これが /ROOT/... のような仮想化されたパスとして現れます。これを使用できない場合は、Vite の例と同様に wasmUrl でウェブビルドを指定してください。
既知のフェッチに関する制限
- Node.js のデフォルトの
connectTimeoutは 10 秒ですが、応答が遅いノードもあります。storageNodeClientOptionsでカスタムのfetch(例:undiciのAgent({ connectTimeout }))を使って、タイムアウトを延長してください。 - Bun では、
abortシグナルを送るとリクエストは応答しなくなりますが、拒否される前にプロミスが完了まで待機し続けます。
ルール
SuiGrpcClientを拡張します。new SuiGrpcClient({ network, baseUrl }).$extend(walrus())を使ってください。JSON-RPC クライアントは非推奨です。- SDK にあるメソッド名を使います。 ファイルとキルトには
getFiles/writeFiles、生の BLOB にはreadBlob/writeBlob、手順に沿った書き込みにはwriteFilesFlow/writeBlobFlowを使ってください。storeBlobはありません。 - 書き込みには必ず
epochs、deletable、signerを渡します。signerには、ガス用の SUI と、ストレージおよび書き込み手数料用の WAL が必要です。 - ブラウザーではアップロードリレーを使います。
uploadRelay.hostを設定し、リレーの/v1/tip-configに対応できる十分な値をsendTip.maxに設定してください。 - BLOB に依存する前に検証します。
Blobオブジェクトのcertified_epoch、storage.end_epoch、deletableを確認してください。 RetryableWalrusClientErrorが発生したらリセットします。client.walrus.reset()を呼び出して再試行してください。成功が保証されるわけではありませんが、エポック変更後に古くなる状態をクリアできます。- 機密データはアップロード前に暗号化します。 Walrus の BLOB はすべて公開されています。
よくある間違い
storeBlobを呼び出す。 このメソッドはありません。writeBlob(生のバイト列)またはwriteFiles(ファイル。キルトとして保存されます)を使ってください。- 生の文字列やオブジェクトを BLOB として渡す。
writeBlobが受け取るのはUint8Arrayです。WalrusFile.fromが受け取るのはUint8Array、Blob、または文字列です。JSON はnew TextEncoder().encode(JSON.stringify(obj))で変換してください。 - Vite で
optimizeDeps.excludeを使う。 ドキュメントに記載されている修正方法は、@mysten/walrus-wasm/web/walrus_wasm_bg.wasmの?urlインポートとwasmUrlオプションを使う方法です。Next.js のルートではserverExternalPackagesを使ってください。 - ブラウザーで、1 回のクリックから register と certify の署名を行う。 2 回目のポップアップはブロックされます。トランザクションごとにユーザー操作が 1 回必要となるように、
writeFilesFlowを使ってください。 writeFilesで単一ファイルを多数書き込む。 呼び出しごとに 1 つのキルトが作成されます。ファイルをまとめて 1 回の呼び出しで渡してください。- 書き込み結果を耐久性の証明として扱う。 登録済みの BLOB は認証済みとは限りません。
getBlobObjectでBlobオブジェクトを読み戻し、certified_epochを確認してください。 Tip amount exceeds the maximum allowed tip。sendTip.maxを引き上げ、リレーの/v1/tip-configを確認してください。reset()せずに再試行する。 エポック変更後はクライアントにキャッシュされた委員会情報が古くなっています。RetryableWalrusClientErrorが発生した場合は、再試行する前にリセットしてください。- システムオブジェクト ID をハードコードする。 SDK にはネットワークごとの ID が含まれています。
packageConfigはカスタムデプロイの場合にのみ使ってください。