From sui-dev-agents
Stores and retrieves files using Walrus, SUI's decentralized blob storage. Covers NFT metadata/images, game assets, media files, and Walrus Sites (on-chain web hosting).
How this skill is triggered — by the user, by Claude, or both
Slash command
/sui-dev-agents:sui-walrusThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Decentralized blob storage for NFTs, media, and large files.**
Decentralized blob storage for NFTs, media, and large files.
Targets: @mysten/walrus 1.1.7 (^1.1), @mysten/sui 2.17.0 (^2.16). Tested: 2026-05-20.
Compatibility notes: @mysten/[email protected] declares @mysten/sui ^2.16.0 as a peer dependency. Do not install on top of a sui 1.x project — npm will pull a second sui copy and you will get dual-SuiClient type errors. The walrus JS SDK only works against SuiGrpcClient / SuiJsonRpcClient from sui 2.x. Run npm ls @mysten/sui first — if 1.x is present, decide before installing: upgrade the project to sui 2.x, or stay on legacy walrus tooling (CLI only).
Walrus provides:
There are two integration paths. Use the JS SDK for in-app uploads (browser/Node) where the user pays for their own storage. Use publishers/aggregators (HTTP) when you want the lightest client — most apps should start there. The CLI is for local tooling and ops.
npm install @mysten/walrus @mysten/sui
Setup — extend a sui 2.x client with the walrus plugin:
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());
Read a blob:
// @check:skip
const bytes = await client.walrus.readBlob({ blobId });
Write a blob (requires a Signer and SUI for gas + WAL for storage fees):
// @check:skip
import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
const signer = Ed25519Keypair.fromSecretKey(/* ... */);
const { blobId } = await client.walrus.writeBlob({
blob: new Uint8Array(/* ... */),
deletable: false,
epochs: 3,
signer,
});
For crash-recoverable uploads use writeBlobFlow / writeFilesFlow (see the package README — the flow API exposes register / upload / certify steps with onStep and resume hooks). Catch RetryableWalrusClientError to reset cached state on epoch changes.
Reading/writing raw blobs through the SDK requires hundreds to thousands of node requests per blob. For high-traffic apps, prefer the upload-relay or publishers/aggregators path.
# Install Walrus CLI
cargo install walrus-cli
# Configure network
walrus config --network testnet
# Upload file to Walrus
walrus upload myimage.png
# Returns blob ID: bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi
module nft::metadata {
use sui::object::{Self, UID};
use std::string::String;
public struct NFT has key, store {
id: UID,
name: String,
walrus_blob_id: vector<u8>, // Store blob ID
}
public fun create_nft(
name: String,
walrus_blob_id: vector<u8>,
ctx: &mut TxContext
): NFT {
NFT {
id: object::new(ctx),
name,
walrus_blob_id,
}
}
public fun metadata_url(nft: &NFT): String {
// Construct Walrus URL
string::utf8(b"walrus://")
.append(string::utf8(nft.walrus_blob_id))
}
}
Use the real @mysten/walrus extension on a v2 Sui client (see the $extend(walrus()) examples earlier in this file). There is no separate @walrus-sdk/client package — that name is fabricated. A minimal upload + Move-call pattern looks like:
// @check:skip
import { SuiGrpcClient } from '@mysten/sui/grpc';
import { walrus } from '@mysten/walrus';
import { Transaction } from '@mysten/sui/transactions';
const client = new SuiGrpcClient({
network: 'testnet',
baseUrl: 'https://fullnode.testnet.sui.io:443',
}).$extend(walrus());
async function uploadNFTMetadata(file: File, signer) {
const bytes = new Uint8Array(await file.arrayBuffer());
const { blobId } = await client.walrus.writeBlob({
blob: bytes,
deletable: false,
epochs: 5,
signer,
});
const tx = new Transaction();
tx.moveCall({
target: `${PACKAGE_ID}::nft::create_nft`,
arguments: [tx.pure.string('My NFT'), tx.pure.string(blobId)],
});
return blobId;
}
function NFTImage({ blobId }: { blobId: string }) {
const url = `https://walrus-testnet.storage/${blobId}`;
return <img src={url} alt="NFT" />;
}
❌ Storing full URL in Move contract
❌ No retry logic on upload failure
❌ Uploading without checksum verification
❌ Hardcoding Walrus gateway URLs
❌ Not handling large file uploads
Query latest Walrus docs via the in-repo sui-docs-query skill (not an SDK function — invoke the skill from Claude Code with args like type=docs target=walrus query="blob upload API and storage patterns").
encodeQuilt), converting blob IDs (int ↔ string), or needing network package-config constants (walrus ≥1.1.7)Decentralized, permanent storage for your SUI NFTs and dApps!
npx claudepluginhub first-mover-tw/sui-dev-agents --plugin sui-dev-agentsProvides behavioral guidelines to reduce common LLM coding mistakes, focusing on simplicity, surgical changes, assumption surfacing, and verifiable success criteria.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.