advanced
+300 XP

Walrus Storage & Attachments

Use Walrus decentralized storage to send file attachments through messaging channels.

Lesson Syllabus

Walrus Decentralized Storage
🦭

What is Walrus?

Walrus is Sui's decentralized storage layer for storing large blobs of data -- images, files, videos -- off-chain but with on-chain references. Unlike storing data directly in Move objects (expensive and size-limited), Walrus uses **erasure coding** to split files across storage nodes, providing redundancy and availability without replicating the full file everywhere. Each uploaded blob receives a unique **blobId** that you can reference from Sui objects.

📦

Content Addressing and Blob Storage

Walrus uses **content addressing**: the blobId is a cryptographic hash of the file contents. This means (1) identical files have the same blobId (deduplication for free), (2) you can verify file integrity by re-hashing and comparing, and (3) blobIds are immutable references -- the data behind a blobId never changes. When you upload a file, Walrus returns a `blobId` and a `registeredEpoch` indicating when storage begins. Blobs are stored for a configurable number of epochs.

🔧

WalrusClient Setup

The `@mysten/walrus` SDK provides `WalrusClient` for uploading and downloading blobs. Initialize it with an `aggregatorUrl` (for reading blobs) and a `publisherUrl` (for writing blobs). These URLs point to Walrus aggregator and publisher services. You also need a `SuiClient` for on-chain operations like registering blob metadata. Once configured, uploading is a single `store()` call and downloading is a single `read()` call.

Uploading Attachments
📤

Using WalrusClient.store()

To upload a file, convert it to a `Uint8Array` or `Blob` and pass it to `walrusClient.store()`. The method handles erasure coding, distributes fragments to storage nodes, and returns a response containing the `blobId`. You specify `epochs` (how long to store) and the `signer` (who pays for storage). The returned `blobId` is what you store in your Sui message object as a reference to the attachment.

📁

Creating a Blob from File Input

In a web application, files come from `<input type='file'>` elements. You read the file using `file.arrayBuffer()`, convert to `Uint8Array`, then upload. For large files, consider showing a progress indicator. Always validate file size and type before uploading -- Walrus has per-blob size limits and you should enforce your own limits (e.g., 10MB max for chat attachments) for good UX.

🔗

Attaching BlobId to Messages

After uploading to Walrus, you get a `blobId`. Now attach it to your messaging channel by calling a Move function that stores the blobId, file name, MIME type, and size in the message object. This creates an on-chain record linking the message to its Walrus-stored attachment. Recipients read the blobId from the message object and download the file from Walrus.

Downloading and Displaying
📥

Downloading Blobs with WalrusClient.read()

To download an attachment, call `walrusClient.read({ blobId })` with the blobId stored in the message object. Walrus fetches fragments from storage nodes, reconstructs the full blob using erasure coding, and returns the raw bytes as a `Uint8Array`. If the blob was encrypted with Seal before upload, you must decrypt the bytes after downloading. The read operation is free -- no gas is required to download from Walrus.

🖼️

Reconstructing and Displaying Files

Once you have the raw bytes and metadata (fileName, mimeType), reconstruct the file for display. For images, create an object URL with `URL.createObjectURL(new Blob([bytes], { type: mimeType }))`. For downloadable files, create a temporary anchor element and trigger a click. Always clean up object URLs with `URL.revokeObjectURL()` to prevent memory leaks. Handle different MIME types gracefully -- show image previews inline, offer download buttons for other types.