--- name: shelby-dapp-builder description: Expert on building decentralized applications with Shelby Protocol storage on Aptos. Helps with dApp architecture, wallet integration (Petra), browser SDK usage, React/Vue integration, file uploads, content delivery, and building Shelby-powered applications. Triggers on keywords Shelby dApp, build on Shelby, Shelby application, Petra wallet, browser storage, web3 app, decentralized app Shelby, React Shelby, Vue Shelby. allowed-tools: Read, Write, Edit, Grep, Glob, Bash model: sonnet --- # Shelby DApp Builder ## Purpose Guide developers in building decentralized applications (dApps) that leverage Shelby Protocol for storage. Covers wallet integration, browser SDK usage, frontend frameworks (React, Vue), user authentication, and complete dApp architecture patterns. ## When to Use Auto-invoke when users ask about: - **DApp Development** - Build Shelby app, create dApp, web3 application - **Browser Integration** - Browser SDK, client-side uploads, web app - **Wallet Integration** - Petra wallet, Aptos wallet, authentication - **Frontend Frameworks** - React Shelby, Vue Shelby, Next.js integration - **User Experience** - File uploads, download flows, progress tracking - **Architecture** - DApp architecture, frontend/backend split, design patterns ## Knowledge Base DApp development documentation: ``` .claude/skills/blockchain/aptos/docs/ ``` Key files: - `sdks_typescript_browser.md` - Browser SDK overview - `sdks_typescript_browser_guides_upload.md` - Upload workflows - `tools_wallets_petra-setup.md` - Wallet integration - `sdks_typescript_acquire-api-keys.md` - API key setup - `protocol.md` - Protocol fundamentals ## Core DApp Architecture ### Three-Layer Pattern ``` ┌─────────────────────────────────────────────┐ │ Frontend (Browser) │ │ - React/Vue/vanilla JS │ │ - Shelby Browser SDK │ │ - Wallet integration (Petra) │ │ - User interface │ └──────────────────┬──────────────────────────┘ │ HTTPS ↓ ┌─────────────────────────────────────────────┐ │ Shelby RPC Servers │ │ - Blob storage/retrieval │ │ - Erasure coding │ │ - Payment processing │ └──────────────────┬──────────────────────────┘ │ ↓ ┌─────────────────────────────────────────────┐ │ Aptos Blockchain │ │ - Smart contracts │ │ - Wallet accounts │ │ - State coordination │ └─────────────────────────────────────────────┘ ``` ### Key Components **1. Wallet Integration** - User authentication via Petra wallet - Transaction signing - Account management - Balance checking **2. Shelby Browser SDK** - File uploads from browser - Blob downloads - Session management - Progress tracking **3. Frontend Framework** - React, Vue, or vanilla JavaScript - File upload UI - Content gallery - User feedback ## Wallet Integration (Petra) ### Setup **Install Petra Wallet:** ``` Chrome Extension: https://petra.app/ ``` **Detect Wallet:** ```typescript // Check if Petra is installed const isPetraInstalled = 'aptos' in window; if (!isPetraInstalled) { alert('Please install Petra Wallet'); window.open('https://petra.app/', '_blank'); } ``` ### Connect Wallet ```typescript import { Network } from '@aptos-labs/ts-sdk'; class WalletManager { async connect(): Promise { try { // Request wallet connection const response = await window.aptos.connect(); // Get account address const account = await window.aptos.account(); console.log('Connected:', account.address); return account.address; } catch (error) { console.error('Wallet connection failed:', error); throw error; } } async disconnect(): Promise { await window.aptos.disconnect(); } async getAccount(): Promise { const account = await window.aptos.account(); return account; } async getNetwork(): Promise { const network = await window.aptos.network(); return network; } async signAndSubmitTransaction(payload: any): Promise { const response = await window.aptos.signAndSubmitTransaction(payload); return response; } } export const walletManager = new WalletManager(); ``` ### Check Balance ```typescript async function checkBalance(address: string): Promise { const response = await fetch( `https://api.shelbynet.shelby.xyz/v1/accounts/${address}/resources` ); const resources = await response.json(); // Find APT and ShelbyUSD balances const aptCoin = resources.find( r => r.type === '0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin>' ); const shelbyUSD = resources.find( r => r.type.includes('ShelbyUSD') ); return { apt: aptCoin?.data?.coin?.value || '0', shelbyUSD: shelbyUSD?.data?.coin?.value || '0' }; } ``` ## Browser SDK Integration ### Installation ```bash npm install @shelby-protocol/sdk @aptos-labs/ts-sdk ``` ### Initialize SDK ```typescript import { ShelbyClient } from '@shelby-protocol/sdk/browser'; import { Network, Account, Ed25519PrivateKey } from '@aptos-labs/ts-sdk'; class ShelbyService { private client: ShelbyClient; private account: Account; async initialize(walletAddress: string): Promise { // For wallet-based signing, use wallet provider // For API key usage (server-side), create account differently this.client = new ShelbyClient({ network: Network.SHELBYNET, apiKey: process.env.NEXT_PUBLIC_SHELBY_API_KEY // Optional }); } getClient(): ShelbyClient { return this.client; } } export const shelbyService = new ShelbyService(); ``` ### File Upload from Browser ```typescript class FileUploader { async uploadFile( file: File, onProgress?: (progress: number) => void ): Promise { // Read file as ArrayBuffer const arrayBuffer = await file.arrayBuffer(); const data = new Uint8Array(arrayBuffer); // Generate blob name const blobName = `uploads/${Date.now()}-${file.name}`; // Set expiration (30 days from now) const expirationTimestamp = Date.now() + 30 * 24 * 60 * 60 * 1000; // Upload to Shelby await shelbyService.getClient().uploadBlob({ blobName, data, expirationTimestamp, onProgress: (progressEvent) => { const percentage = (progressEvent.loaded / progressEvent.total) * 100; onProgress?.(percentage); } }); return blobName; } async uploadMultiple( files: FileList, onProgress?: (file: string, progress: number) => void ): Promise { const uploads = Array.from(files).map(async (file) => { const blobName = await this.uploadFile(file, (progress) => { onProgress?.(file.name, progress); }); return blobName; }); return await Promise.all(uploads); } } export const fileUploader = new FileUploader(); ``` ### Download Blob ```typescript async function downloadBlob(blobName: string, account: string): Promise { const data = await shelbyService.getClient().getBlob(blobName); // Convert to Blob for browser download return new Blob([data]); } async function downloadAndSave(blobName: string, account: string, filename: string) { const blob = await downloadBlob(blobName, account); // Create download link const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } ``` ## React Integration ### Upload Component ```typescript import React, { useState } from 'react'; import { fileUploader } from './shelby-service'; export function FileUploadComponent() { const [selectedFile, setSelectedFile] = useState(null); const [uploading, setUploading] = useState(false); const [progress, setProgress] = useState(0); const [blobName, setBlobName] = useState(null); const handleFileSelect = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { setSelectedFile(file); } }; const handleUpload = async () => { if (!selectedFile) return; setUploading(true); setProgress(0); try { const result = await fileUploader.uploadFile( selectedFile, (percentage) => { setProgress(percentage); } ); setBlobName(result); alert('Upload successful!'); } catch (error) { console.error('Upload failed:', error); alert('Upload failed: ' + error.message); } finally { setUploading(false); } }; return (

Upload to Shelby

{selectedFile && (

Selected: {selectedFile.name}

Size: {(selectedFile.size / 1024 / 1024).toFixed(2)} MB

)} {uploading && (
{progress.toFixed(1)}%
)} {blobName && (

Blob Name: {blobName}

Stored successfully on Shelby!

)}
); } ``` ### Gallery Component ```typescript import React, { useState, useEffect } from 'react'; interface BlobMetadata { name: string; size: number; expirationTimestamp: number; url: string; } export function BlobGalleryComponent({ account }: { account: string }) { const [blobs, setBlobs] = useState([]); const [loading, setLoading] = useState(true); useEffect(() => { loadBlobs(); }, [account]); const loadBlobs = async () => { try { // Query indexer or smart contract for user's blobs const response = await fetch( `https://api.shelbynet.shelby.xyz/indexer/v1/blobs/${account}` ); const data = await response.json(); const blobList = data.map((blob: any) => ({ name: blob.name, size: blob.size, expirationTimestamp: blob.expiration, url: `https://api.shelbynet.shelby.xyz/shelby/v1/blobs/${account}/${blob.name}` })); setBlobs(blobList); } catch (error) { console.error('Failed to load blobs:', error); } finally { setLoading(false); } }; const handleDownload = async (blob: BlobMetadata) => { const filename = blob.name.split('/').pop() || 'download'; await downloadAndSave(blob.name, account, filename); }; if (loading) { return
Loading your blobs...
; } return (

Your Blobs

{blobs.length === 0 ? (

No blobs found. Upload your first file!

) : (
{blobs.map((blob) => (

{blob.name}

Size: {(blob.size / 1024 / 1024).toFixed(2)} MB

Expires: {new Date(blob.expirationTimestamp).toLocaleDateString()}

{blob.name.match(/\.(jpg|jpeg|png|gif)$/i) && ( {blob.name} )}
))}
)}
); } ``` ### Complete React App ```typescript import React, { useState, useEffect } from 'react'; import { walletManager } from './wallet-manager'; import { shelbyService } from './shelby-service'; import { FileUploadComponent } from './FileUploadComponent'; import { BlobGalleryComponent } from './BlobGalleryComponent'; export function App() { const [connected, setConnected] = useState(false); const [account, setAccount] = useState(null); const [balance, setBalance] = useState({ apt: '0', shelbyUSD: '0' }); useEffect(() => { checkWalletConnection(); }, []); const checkWalletConnection = async () => { try { const isConnected = await window.aptos?.isConnected(); if (isConnected) { const account = await window.aptos.account(); setAccount(account.address); setConnected(true); await loadBalance(account.address); await shelbyService.initialize(account.address); } } catch (error) { console.error('Failed to check wallet:', error); } }; const handleConnect = async () => { try { const address = await walletManager.connect(); setAccount(address); setConnected(true); await loadBalance(address); await shelbyService.initialize(address); } catch (error) { console.error('Connection failed:', error); } }; const handleDisconnect = async () => { await walletManager.disconnect(); setConnected(false); setAccount(null); }; const loadBalance = async (address: string) => { const balances = await checkBalance(address); setBalance(balances); }; return (

Shelby Storage DApp

{!connected ? ( ) : (

Account: {account?.slice(0, 6)}...{account?.slice(-4)}

APT: {(parseInt(balance.apt) / 1e8).toFixed(4)}

ShelbyUSD: {(parseInt(balance.shelbyUSD) / 1e8).toFixed(4)}

)}
{connected && account ? (
) : (

Please connect your Petra wallet to use this app

)}
); } ``` ## Vue Integration ### Upload Component (Vue 3) ```vue ``` ## Complete DApp Examples ### Example 1: Decentralized Image Gallery ```typescript // Image Gallery DApp class ImageGalleryDApp { async uploadImage(imageFile: File, metadata: any) { // Resize/compress image if needed const optimized = await this.optimizeImage(imageFile); // Upload to Shelby const blobName = `gallery/${metadata.albumId}/${Date.now()}.jpg`; await shelbyService.getClient().uploadBlob({ blobName, data: optimized, expirationTimestamp: Date.now() + 365 * 24 * 60 * 60 * 1000 }); // Store metadata on-chain or in separate index await this.storeMetadata(blobName, metadata); return blobName; } async loadGallery(albumId: string): Promise { // Query blobs by prefix const images = await this.queryBlobs(`gallery/${albumId}/`); return images.map(blob => ({ url: this.getBlobURL(blob.name), metadata: blob.metadata, thumbnail: this.getBlobURL(blob.name, { size: 'thumbnail' }) })); } getBlobURL(blobName: string, options?: any): string { const baseUrl = `https://api.shelbynet.shelby.xyz/shelby/v1/blobs`; return `${baseUrl}/${this.account}/${blobName}`; } } ``` ### Example 2: Decentralized Video Platform ```typescript // Video Streaming DApp class VideoStreamingDApp { async uploadVideo(videoFile: File, metadata: any) { // Transcode to HLS format const hlsSegments = await this.transcodeToHLS(videoFile); // Upload each segment const videoId = Date.now().toString(); const uploadPromises = hlsSegments.map(segment => shelbyService.getClient().uploadBlob({ blobName: `videos/${videoId}/${segment.name}`, data: segment.data, expirationTimestamp: Date.now() + 90 * 24 * 60 * 60 * 1000 }) ); await Promise.all(uploadPromises); // Upload playlist manifest await this.uploadPlaylist(videoId, hlsSegments); // Store video metadata await this.storeVideoMetadata(videoId, metadata); return videoId; } getStreamURL(videoId: string): string { return `https://api.shelbynet.shelby.xyz/shelby/v1/blobs/${this.account}/videos/${videoId}/playlist.m3u8`; } } ``` ### Example 3: Decentralized File Sharing ```typescript // File Sharing DApp class FileSharingDApp { async shareFile(file: File, recipients: string[], expireDays: number) { // Upload file to Shelby const shareId = this.generateShareId(); const blobName = `shares/${shareId}/${file.name}`; await shelbyService.getClient().uploadBlob({ blobName, data: await file.arrayBuffer(), expirationTimestamp: Date.now() + expireDays * 24 * 60 * 60 * 1000 }); // Create shareable link const shareLink = `https://myapp.com/share/${shareId}`; // Store access control (on-chain or off-chain) await this.storeAccessControl(shareId, recipients); return shareLink; } async accessSharedFile(shareId: string, userAccount: string) { // Check access control const hasAccess = await this.checkAccess(shareId, userAccount); if (!hasAccess) { throw new Error('Access denied'); } // Download file const metadata = await this.getShareMetadata(shareId); return await downloadBlob(metadata.blobName, metadata.owner); } } ``` ## Best Practices ### 1. User Experience **Progress Feedback:** ```typescript // Always show upload/download progress const [progress, setProgress] = useState(0); await uploadFile(file, (percent) => { setProgress(percent); // Update UI }); ``` **Error Handling:** ```typescript try { await uploadFile(file); } catch (error) { if (error.code === 'INSUFFICIENT_FUNDS') { alert('Please add ShelbyUSD to your wallet'); } else if (error.code === 'FILE_TOO_LARGE') { alert('File size exceeds limit'); } else { alert('Upload failed. Please try again.'); } } ``` **Loading States:** ```typescript // Show loading indicators const [loading, setLoading] = useState(false); // Disable actions during operations ``` ### 2. Performance Optimization **Image Optimization:** ```typescript async function optimizeImage(file: File): Promise { return new Promise((resolve) => { const img = new Image(); img.src = URL.createObjectURL(file); img.onload = () => { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d')!; // Resize if needed const maxWidth = 1920; const maxHeight = 1080; let { width, height } = img; if (width > maxWidth) { height = (height * maxWidth) / width; width = maxWidth; } canvas.width = width; canvas.height = height; ctx.drawImage(img, 0, 0, width, height); // Convert to blob with compression canvas.toBlob( (blob) => { blob!.arrayBuffer().then(buffer => { resolve(new Uint8Array(buffer)); }); }, 'image/jpeg', 0.85 // Quality ); }; }); } ``` **Lazy Loading:** ```typescript // Load blobs on-demand const [visibleBlobs, setVisibleBlobs] = useState([]); useEffect(() => { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { // Load blob when visible loadBlob(entry.target.dataset.blobName); } }); }); // Observe blob containers }, []); ``` ### 3. Security **Wallet Integration:** ```typescript // Always verify wallet connection const isConnected = await window.aptos?.isConnected(); if (!isConnected) { throw new Error('Please connect wallet'); } // Verify network const network = await window.aptos.network(); if (network !== 'shelbynet') { alert('Please switch to Shelby Network'); } ``` **Input Validation:** ```typescript // Validate file types const allowedTypes = ['image/jpeg', 'image/png', 'image/gif']; if (!allowedTypes.includes(file.type)) { throw new Error('File type not supported'); } // Validate file size const maxSize = 100 * 1024 * 1024; // 100MB if (file.size > maxSize) { throw new Error('File too large'); } ``` ### 4. Cost Management **Estimate Costs:** ```typescript async function estimateUploadCost(fileSize: number, durationDays: number): Promise { // Rough estimation based on size and duration const costPerMBPerDay = 0.001; // Example rate in ShelbyUSD const sizeMB = fileSize / (1024 * 1024); return sizeMB * durationDays * costPerMBPerDay; } // Show cost before upload const cost = await estimateUploadCost(file.size, 30); const confirmed = confirm(`This upload will cost ~${cost.toFixed(4)} ShelbyUSD. Continue?`); ``` ## Process for Helping Users ### 1. Understand App Requirements **Questions:** - What type of dApp are you building? - What files/data need storage? - Who are your users? - What's the user flow? - Mobile or desktop (or both)? ### 2. Design Architecture **Choose Pattern:** - Pure client-side (browser SDK only) - Hybrid (frontend + backend API) - Server-side rendering with Shelby **Plan Components:** - Authentication/wallet - Upload interface - Download/display logic - State management ### 3. Provide Implementation **Show:** - Complete code examples - Component structure - Integration patterns - Best practices ### 4. Testing & Deployment **Guide:** - Local testing workflow - Testnet deployment - Production considerations - Monitoring ## Response Style - **Framework-aware** - Provide React, Vue, or vanilla JS examples - **Complete** - Show full component implementations - **User-focused** - Emphasize UX and user feedback - **Practical** - Working code ready to adapt - **Modern** - Use current best practices (hooks, composition API) ## Example Interaction ``` User: "How do I build a React app for uploading images to Shelby?" Response: 1. Show wallet integration (Petra) 2. Provide complete React upload component 3. Show gallery/display component 4. Include image optimization 5. Add progress tracking and error handling 6. Suggest deployment strategy 7. Reference: sdks_typescript_browser_guides_upload.md ``` ## Limitations - Browser SDK has different constraints than Node.js - Wallet-based signing differs from API key approach - CORS considerations for direct API calls - Be clear about testnet vs mainnet differences ## Follow-up Suggestions - State management (Redux, Zustand) - Mobile app development (React Native) - PWA features (offline support) - Testing strategies (Jest, Playwright) - Performance monitoring - User analytics