Skip to main content

Overview

The Walrus helper utilities provide robust functions for uploading and fetching content from Walrus storage. These utilities implement automatic fallback across multiple publisher and aggregator endpoints for reliability.

Configuration

Publisher Endpoints

TUNA uses multiple Walrus publisher endpoints with automatic fallback:
const WALRUS_PUBLISHERS = [
  'https://walrus-testnet-publisher.nodes.guru/v1/blobs',
  'https://walrus-testnet-publisher.stakely.io/v1/blobs',
  'https://publisher.walrus-testnet.walrus.space/v1/blobs',
  'https://walrus-testnet-publisher.everstake.one/v1/blobs',
  'https://walrus-testnet-publisher.chainbase.online/v1/blobs',
  'https://publisher.testnet.walrus.atalma.io/v1/blobs',
];

Aggregator Endpoints

const WALRUS_AGGREGATORS = [
  'https://walrus-testnet-aggregator.nodes.guru/v1/blobs',
  'https://walrus-testnet-aggregator.stakely.io/v1/blobs',
  'https://aggregator.walrus-testnet.walrus.space/v1/blobs',
  'https://walrus-testnet-aggregator.everstake.one/v1/blobs',
  'https://walrus-testnet-aggregator.chainbase.online/v1/blobs',
  'https://aggregator.testnet.walrus.atalma.io/v1/blobs',
];
If one endpoint fails, the function automatically tries the next one in the list.

Upload Functions

uploadToWalrus

Upload JSON content to Walrus with automatic fallback across publishers.
async function uploadToWalrus(
  content: WalrusArticleContent | WalrusCommentContent
): Promise<string>
content
WalrusArticleContent | WalrusCommentContent
required
Content object to upload (will be JSON stringified)
Returns: Promise<string> - The Walrus blob ID Implementation Details:
  • Content is JSON stringified before upload
  • Tries each publisher endpoint sequentially until success
  • Adds ?epochs=30 parameter for blob retention
  • Handles multiple response formats from Walrus API
  • Throws error if all publishers fail
Response Format Handling:
const blobId = 
  data.newlyCreated?.blobObject?.blobId ||      // New blob
  data.alreadyCertified?.blobId ||               // Already exists
  data.blobObject?.blobId;                       // Alternative format
Example:
import { uploadToWalrus } from '@/lib/walrus';
import type { WalrusArticleContent } from '@/types';

const content: WalrusArticleContent = {
  title: 'My Article',
  subtitle: 'An interesting story',
  content: 'Article content...',
  coverImage: 'blob_id_for_image',
};

try {
  const blobId = await uploadToWalrus(content);
  console.log('Uploaded to Walrus:', blobId);
} catch (error) {
  console.error('Upload failed:', error);
}

uploadImageToWalrus

Upload image files to Walrus storage.
async function uploadImageToWalrus(file: File): Promise<string>
file
File
required
Image file object from file input or drag-and-drop
Returns: Promise<string> - The Walrus blob ID for the image Implementation Details:
  • Converts file to ArrayBuffer for binary upload
  • Preserves original file MIME type
  • Uses same fallback logic as uploadToWalrus
  • Stores for 30 epochs
Example:
import { uploadImageToWalrus } from '@/lib/walrus';

const handleImageUpload = async (file: File) => {
  try {
    const blobId = await uploadImageToWalrus(file);
    console.log('Image uploaded:', blobId);
    return blobId;
  } catch (error) {
    console.error('Image upload failed:', error);
    throw error;
  }
};

// Usage with file input
<input 
  type="file" 
  accept="image/*"
  onChange={(e) => {
    const file = e.target.files?.[0];
    if (file) handleImageUpload(file);
  }}
/>

Fetch Functions

fetchFromWalrus

Fetch content from Walrus with automatic fallback across aggregators.
async function fetchFromWalrus<T = any>(blobId: string): Promise<T>
blobId
string
required
The Walrus blob ID to fetch
T
generic
default:"any"
Type parameter for the expected return type
Returns: Promise<T> - The parsed JSON content Implementation Details:
  • Validates blob ID format before attempting fetch
  • Tries each aggregator endpoint sequentially
  • Automatically parses JSON response
  • Throws error if all aggregators fail
Blob ID Validation:
// Invalid blob IDs are rejected early:
- Empty or null blob IDs
- Length < 30 characters
- Contains spaces
- Placeholder values like "blob_id"
Example:
import { fetchFromWalrus } from '@/lib/walrus';
import type { WalrusArticleContent } from '@/types';

const loadArticle = async (blobId: string) => {
  try {
    const content = await fetchFromWalrus<WalrusArticleContent>(blobId);
    console.log('Article:', content.title);
    return content;
  } catch (error) {
    console.error('Failed to fetch article:', error);
    throw error;
  }
};

getWalrusUrl

Get the public URL for a Walrus blob.
function getWalrusUrl(blobId: string): string
blobId
string
required
The Walrus blob ID
Returns: string - Public URL to access the blob Implementation:
// Uses the third aggregator (walrus.space) as default
return `${WALRUS_AGGREGATORS[2]}/${blobId}`;
Example:
import { getWalrusUrl } from '@/lib/walrus';

const url = getWalrusUrl('abc123...');
// Returns: "https://aggregator.walrus-testnet.walrus.space/v1/blobs/abc123..."

// Use in img tags
<img src={getWalrusUrl(article.coverImage)} alt="Cover" />

Error Handling

All Walrus functions implement robust error handling:
try {
  const blobId = await uploadToWalrus(content);
  // Success
} catch (error) {
  // Error message includes number of failed attempts
  // "Walrus upload failed on all 6 publishers"
  console.error(error);
}

Type Definitions

WalrusArticleContent

type WalrusArticleContent = {
  title: string;
  subtitle?: string;
  content: string;
  coverImage?: string;
  tags?: string[];
};

WalrusCommentContent

type WalrusCommentContent = {
  text?: string;
  mediaType?: string;
  mediaBlobId?: string;
};

Best Practices

Storage Duration: All uploads use epochs=30 parameter, which stores data for 30 epochs on Walrus testnet. Plan for re-upload or archival strategies for long-term storage.
Blob ID Validation: Always validate blob IDs before attempting to fetch. Invalid IDs will fail early without consuming API requests.
Error Recovery: The automatic fallback mechanism makes uploads and fetches resilient. However, you should still implement user-facing error messages for when all endpoints fail.

Source Reference

All Walrus helper functions are implemented in:
src/lib/walrus.ts

Build docs developers (and LLMs) love