This document covers Walrus storage integration for the Splash event ticketing platform, including blob storage, Sui structures, and Walrus Sites for frontend hosting.
- Overview
- Core Concepts
- Storing Event Data
- Sui Structures
- Walrus Sites for Frontend
- Integration with Splash
- References
Walrus is a decentralized storage platform built on Sui that provides:
- Blob storage: Store any file type as immutable blobs
- Epoch-based duration: Storage paid for specific time periods
- Sui integration: Blob metadata tracked as Sui objects
- High availability: Erasure coding across storage nodes
- Walrus Sites: Decentralized web hosting
Reference: Walrus Documentation | Getting Started
A blob is an immutable array of bytes. Key properties:
| Property | Description |
|---|---|
| Blob ID | Content-addressed identifier (same content = same ID) |
| Sui Object ID | Corresponding Sui object for metadata management |
| Epochs | Storage duration (Testnet: 1 day, Mainnet: 2 weeks per epoch) |
| Deletable | Whether the blob can be deleted before expiration |
- SUI: Pay transaction fees on Sui
- WAL: Pay for Walrus storage
# Convert SUI to WAL on Testnet
walrus get-wal --context testnet| Network | Epoch Duration | Max Epochs | Use Case |
|---|---|---|---|
| Testnet | 1 day | 53 | Development and testing |
| Mainnet | 2 weeks | 53 | Production |
Warning: Testnet does not guarantee data persistence and may wipe data.
Reference: Available Networks
Reference: Getting Started
# Install suiup tool
curl -sSfL https://raw.githubusercontent.com/Mystenlabs/suiup/main/install.sh | sh
# Install sui and walrus CLI
suiup install sui
suiup install walrus
# Configure for Testnet
curl --create-dirs https://docs.wal.app/setup/client_config.yaml -o ~/.config/walrus/client_config.yamlReference: Client CLI
# Store an event image
walrus store event-banner.png --epochs 52 --context testnet
# Output:
# Blob ID: oehkoh0352bRGNPjuwcy0nye3OLKT649K62imdNAlXg
# Sui object ID: 0x1c086e216c4d35bf4c1ea493aea701260ffa5b0070622b17271e4495a030fe83# Retrieve blob by ID
walrus read <blob-id> --out downloaded-image.png --context testnetWalrus also provides HTTP endpoints for programmatic access:
# Store via HTTP (using aggregator/publisher)
curl -X PUT "https://publisher.walrus-testnet.walrus.space/v1/blobs" \
-H "Content-Type: application/octet-stream" \
--data-binary @event-banner.png
# Read via HTTP
curl "https://aggregator.walrus-testnet.walrus.space/v1/blobs/<blob-id>" \
--output downloaded.pngThe Walrus SDK is a client extension for the Sui SDK:
import { SuiJsonRpcClient } from '@mysten/sui/jsonRpc';
import { getFullnodeUrl } from '@mysten/sui/client';
import { walrus } from '@mysten/walrus';
// Create client with walrus extension
const client = new SuiJsonRpcClient({
url: getFullnodeUrl('testnet'),
network: 'testnet', // Required for walrus
}).$extend(walrus());
// Read a blob
const data = await client.walrus.readBlob({ blobId });
// Write a blob
const { blobId } = await client.walrus.writeBlob({
blob: eventImageBuffer,
deletable: false,
epochs: 3,
signer: keypair, // Pays for storage
});Reference: Walrus SDK | Walrus SDKs | Web API
Walrus uses Sui objects to track blob metadata. These can be queried and used in Move contracts.
/// Reservation for storage for a given period
public struct Storage has key, store {
id: UID,
start_epoch: u32,
end_epoch: u32,
storage_size: u64,
}/// Represents a blob registered on Walrus
public struct Blob has key, store {
id: UID,
registered_epoch: u32,
blob_id: u256,
size: u64,
encoding_type: u8,
/// Epoch when certified (None if pending)
certified_epoch: option::Option<u32>,
storage: Storage,
/// Whether this blob can be deleted
deletable: bool,
}import { SuiClient } from '@mysten/sui/client';
const client = new SuiClient({ url: 'https://fullnode.testnet.sui.io:443' });
// Get blob object details
const blobObject = await client.getObject({
id: suiObjectId,
options: { showContent: true },
});
// Check if certified
const fields = blobObject.data?.content?.fields;
const isCertified = fields?.certified_epoch !== null;
const endEpoch = fields?.storage?.fields?.end_epoch;Walrus emits Sui events for blob lifecycle:
| Event | Description |
|---|---|
BlobRegistered |
Blob registered, awaiting certification |
BlobCertified |
Blob successfully stored and available |
BlobDeleted |
Deletable blob was deleted |
InvalidBlobID |
Incorrectly encoded blob detected |
Reference: Sui Structures | Walrus Contracts
Walrus Sites enables hosting complete web applications on Walrus with Sui-based addressing.
- Static hosting: HTML, CSS, JS, images
- No backend required: Integrate with Sui for dynamic functionality
- SuiNS integration: Human-readable domain names
- Portal access: Browse via
wal.appor self-hosted portals - Per-NFT sites: Create unique sites for each NFT
# Install site builder
suiup install site-builder
# Verify installation
site-builder --help# Build frontend (e.g., Next.js)
npm run build
npm run export # Generate static files
# Publish to Walrus Sites
site-builder publish ./out --epochs 52
# Output:
# Site published!
# Object ID: 0x1234...
# Browse at: https://<object-id>.walrus.site# Link SuiNS name to site
site-builder set-name splash.sui --site-object 0x1234...
# Now accessible at: https://splash.wal.appCreate ws-config.json in your site root:
{
"routes": {
"/events/*": "/events/[id].html",
"/tickets": "/tickets.html",
"/profile": "/profile.html"
},
"fallback": "/index.html"
}# Update existing site
site-builder update ./out --site-object 0x1234...Reference: Walrus Sites Introduction | Tutorial
| Data Type | Storage Location | Notes |
|---|---|---|
| Event images | Walrus blob | Store blob_id in Event object |
| Event metadata | Walrus blob (JSON) | Public event details |
| Encrypted venue details | Walrus blob + Seal | Encrypted, NFT-gated |
| Ticket QR codes | Walrus blob + Seal | Per-ticket encrypted |
| Attendance badges | Walrus blob | Image for SBT |
| Frontend | Walrus Sites | Entire dApp |
async function createEvent(eventData: EventInput, signer: Keypair) {
// 1. Upload event image to Walrus
const { blobId: imageBlobId } = await client.walrus.writeBlob({
blob: eventData.image,
epochs: 53, // Maximum allowed (~2 years on mainnet)
deletable: false,
signer,
});
// 2. Encrypt venue details with Seal
const encryptedVenue = await sealClient.encrypt({
data: eventData.venueDetails,
packageId: SPLASH_PACKAGE,
id: eventData.eventId,
threshold: 2,
});
// 3. Store encrypted venue on Walrus
const { blobId: venueBlobId } = await client.walrus.writeBlob({
blob: encryptedVenue,
epochs: 53, // Maximum allowed
deletable: false,
signer,
});
// 4. Create event on Sui with blob references
const tx = new Transaction();
tx.moveCall({
target: `${SPLASH_PACKAGE}::event::create_event`,
arguments: [
tx.pure.string(eventData.title),
tx.pure.string(eventData.description),
tx.pure.u64(eventData.date),
tx.pure.string(eventData.location),
tx.pure.u64(eventData.capacity),
tx.pure.u64(eventData.price),
tx.pure.u256(imageBlob.blobId),
tx.pure.u256(venueBlob.blobId),
tx.pure.bool(eventData.isPrivate),
tx.pure.string(eventData.requiredBadge || ''),
tx.pure.vector('string', eventData.tags),
],
});
return await client.signAndExecuteTransaction({ transaction: tx });
}async function getEventDetails(event: EventObject) {
// 1. Fetch public image from Walrus (via aggregator for simplicity)
const imageUrl = `https://aggregator.walrus-testnet.walrus.space/v1/blobs/${event.image_blob_id}`;
// Or read directly via SDK:
// const imageData = await client.walrus.readBlob({ blobId: event.image_blob_id });
// 2. If user has ticket, decrypt venue details
if (userHasTicket) {
const encryptedData = await client.walrus.readBlob({
blobId: event.encrypted_details_blob_id
});
const decrypted = await sealClient.decrypt({
data: encryptedData,
sessionKey,
txBytes: /* seal_approve PTB */,
});
return { ...event, venueDetails: decrypted };
}
return event;
}Important: Walrus blobs expire after the specified epochs.
Note: Maximum epochs per purchase is 53 epochs (~2 years on Mainnet, ~53 days on Testnet). For longer storage, you must extend before expiration.
| Event Type | Recommended Epochs | Duration (Mainnet) |
|---|---|---|
| Short-term events | 4-8 | 2-4 months |
| Annual events | 26 | ~1 year |
| Maximum initial | 53 | ~2 years |
| Long-term (with renewal) | 53 + extensions | 2+ years |
Renewal Strategy:
// Extend blob storage before expiration
// Use either `epochs` (add N epochs) or `endEpoch` (set specific end)
const tx = await client.walrus.extendBlob({
blobObjectId: '0x1234...',
epochs: 26, // Add ~1 year on mainnet
});
await suiClient.signAndExecuteTransaction({ transaction: tx, signer });Reference: Available Networks - Maximum 53 epochs per purchase