Skip to Content
Welcome to RitoSwap's documentation!
DAppInternal LibrariesGated Content

Gated Content Library

The Gated Content Library serves as the content management system for RitoSwap’s token gate, assembling all the exclusive content that authenticated users receive. This internal library combines HTML, CSS, JavaScript, and audio data into a cohesive package that creates an immersive experience for token holders.

Overview

When users successfully unlock the token gate with their Colored Key NFT, they gain access to exclusive content that includes the ability to submit a single message and listen to secret crypto-themed music. The Gated Content Library orchestrates this experience by packaging all necessary components into a structured format that the frontend can render seamlessly.

The Token Gate Experience

The gated content represents the culmination of the user’s journey through RitoSwap’s token system. After minting or acquiring a Colored Key NFT and proving ownership through wallet signatures, users are rewarded with access to this exclusive area. The content is designed to be ephemeral—users can submit only one message with their key before it’s marked as used, making each interaction unique and valuable.

      • gatedContent.ts

Content Structure

The getGatedContent function returns a comprehensive content package:

export async function getGatedContent() { return { welcomeText: string, // Welcome message for authenticated users textSubmissionAreaHtml: string, // HTML for the message submission form audioData: { // Audio player configuration headline: string, imageSrc: string, imageAlt: string, description: string, title: string, audioSrc: string, // Signed URL from R2 error?: boolean }, styles: string, // CSS for styling the content script: string // JavaScript for form interaction }; }

Content Components Breakdown

The library assembles four main components that work together to create the gated experience:

1. Welcome Text

The introductory message sets expectations for users entering the exclusive area. It explains the one-time message submission rule and teases the exclusive audio content, creating anticipation and emphasizing the limited nature of the experience.

2. Text Submission Area

A carefully crafted HTML form allows users to submit their single message. The form includes a textarea for message input and a submit button that triggers the signature and submission process. The HTML is designed to be injected safely into the page with proper styling hooks.

3. Audio Data

Configuration for the AudioWrapper component, including metadata about the exclusive track and the signed URL generated by the R2 library. This creates the music player interface that showcases Rito Rhymes’ crypto-themed parody music.

4. Styling and Scripts

Custom CSS creates the cyberpunk aesthetic with glowing borders and animations, while the JavaScript handles form submission logic and connects to the parent component’s submission handler.

API Reference

FunctionSignatureReturnsNotes
getGatedContent(): Promise<GatedContentResponse>Complete content packageAssembles all gated content including signed audio URL

Response Type Structure

interface GatedContentResponse { welcomeText: string textSubmissionAreaHtml: string audioData: AudioData styles: string script: string } interface AudioData { headline: string // "Secret Crypto Music" imageSrc: string // Album cover path imageAlt: string // Accessibility text description: string // Track description title: string // "Hit Me Bitcoin One More Time" audioSrc: string // Signed R2 URL error?: boolean // Audio availability flag }

Implementation Details

Audio URL Generation

The library integrates with the R2 storage system to provide secure audio access:

let audioUrl: string; let audioError = false; try { audioUrl = await generateSignedAudioUrl(); } catch (error) { console.error('Failed to generate signed audio URL:', error); audioUrl = ''; audioError = true; }

This error handling ensures that audio failures don’t break the entire gated experience—users can still submit their message even if the audio is temporarily unavailable.

HTML Content Structure

The text submission area HTML is carefully structured for security and functionality:

<div class="textSubmissionContainer"> <h2 class="textSubmissionTitle">Submit Your Message</h2> <form id="gatedSubmissionForm" class="textSubmissionForm"> <textarea id="gatedTextarea" class="textSubmissionTextarea" placeholder="Enter your message here..." rows="6" ></textarea> <button type="submit" id="gatedSubmitButton" class="textSubmissionButton" > Sign & Submit </button> </form> </div>

Each element has specific IDs and classes that connect to both the styling system and the JavaScript behavior handlers.

Styling System

The CSS creates a cohesive visual experience with the Blade Runner-inspired aesthetic:

.textSubmissionContainer { background: rgba(0, 30, 60, 0.8); border: var(--default-border); box-shadow: 0 0 20px rgba(0, 123, 255, 0.3), 0 0 40px rgba(0, 123, 255, 0.2), 0 0 60px rgba(0, 123, 255, 0.1), inset 0 0 20px rgba(0, 123, 255, 0.1); animation: glowPulse 3s ease-in-out infinite; }

The glowing animation and layered shadows create depth and visual interest, making the exclusive content feel special and otherworldly.

JavaScript Behavior

The embedded script handles form submission with proper validation and error handling:

form.addEventListener('submit', async (e) => { e.preventDefault(); const text = textarea.value.trim(); if (!text) { alert('Please enter some text'); return; } // Disable button and show processing state submitButton.disabled = true; submitButton.textContent = 'Sending...'; submitButton.classList.add('processing'); try { window.handleGatedSubmission(text); } catch (error) { // Re-enable on error submitButton.disabled = false; submitButton.textContent = 'Sign & Submit'; submitButton.classList.remove('processing'); } });

The script connects to the parent component through the global handleGatedSubmission function, which is injected by the GatedContentRenderer.

Integration Flow

The gated content flows through several components in the RitoSwap system:

Step 1: User Unlocks Gate

The GateModal component verifies ownership and signatures, then calls the /api/gate-access endpoint.

Step 2: API Fetches Content

The gate-access API calls getGatedContent() to assemble the content package, including generating the signed audio URL.

Step 3: Content Delivery

The content is passed back through the GateModal to the GatePageWrapper via the onContentReceived callback.

Step 4: Rendering

The GatedContentRenderer component receives the content and renders it, injecting styles, HTML, and executing the script.

Step 5: User Interaction

Users can play the exclusive audio and submit their one-time message, which triggers the signature and submission flow.

Component Integration Example

Here’s how the GatedContentRenderer uses the content:

// In GatedContentRenderer.tsx useEffect(() => { // Inject styles const styleEl = document.createElement("style"); styleEl.textContent = content.styles; document.head.appendChild(styleEl); // Expose submission handler (window as any).handleGatedSubmission = onSubmit; // Inject HTML content containerRef.current.innerHTML = ` <div class="${styles.welcomeText}">${content.welcomeText}</div> <div class="${styles.contentArea}">${content.textSubmissionAreaHtml}</div> `; // Execute script const runner = new Function(content.script); runner(); }, [content, onSubmit]);

Security Considerations

Content Injection Safety

While the library returns HTML and JavaScript as strings, several measures ensure security:

  1. Trusted Source: Content comes from server-side code, not user input
  2. Scoped Execution: JavaScript runs in a controlled context
  3. No User Data: The HTML template doesn’t include any user-provided content
  4. Event Validation: Form submission is validated before processing

Message Submission Security

The submission flow includes multiple security layers:

// In GatePageWrapper handleGatedSubmission const signMessage = `Token Gate Access Request: Token ID: ${tokenId} Address: ${address} Timestamp: ${timestamp} Message: ${text}`; const signature = await signMessageAsync({ message: signMessage });

This creates a tamper-proof record of:

  • Which token was used
  • Who submitted the message
  • When it was submitted
  • The exact message content

Customization Points

Modifying the Welcome Message

The welcome text can be updated to reflect different campaigns or events:

welcomeText: process.env.GATE_WELCOME_MESSAGE || "Welcome inside. You get to send one message with your unused key..."

Styling Variations

The CSS can be modified for special themes:

const seasonalStyles = { default: defaultStyles, halloween: halloweenStyles, holiday: holidayStyles }; styles: seasonalStyles[currentSeason] || seasonalStyles.default

Dynamic Audio Selection

The audio data could be randomized or based on token attributes:

const tracks = [ { title: "Hit Me Bitcoin One More Time", file: "HitMeBitcoin.mp3" }, { title: "Crypto Killed The Radio Star", file: "CryptoKilled.mp3" }, { title: "Sweet Chain O' Mine", file: "SweetChain.mp3" } ]; const selectedTrack = tracks[tokenId % tracks.length];

Error Handling

The library implements graceful degradation for various failure scenarios:

Audio Failure Handling

When R2 storage is unavailable:

if (content.audioData.error || audioError) { return ( <div className={styles.audioError}> <h3>Audio Temporarily Unavailable</h3> <p>Please refresh the page to try again</p> </div> ); }

Script Execution Errors

The renderer wraps script execution in try-catch:

try { const runner = new Function(content.script); runner(); } catch (error) { console.error("Error executing gated content script:", error); }

Submission Failures

The embedded script handles submission errors:

catch (error) { console.error('Submission error:', error); alert('Failed to submit. Please try again.'); // Re-enable form for retry }

Testing Considerations

When testing the gated content system:

Content Assembly Testing

describe('getGatedContent', () => { it('returns complete content structure', async () => { const content = await getGatedContent(); expect(content).toHaveProperty('welcomeText'); expect(content).toHaveProperty('textSubmissionAreaHtml'); expect(content).toHaveProperty('audioData'); expect(content).toHaveProperty('styles'); expect(content).toHaveProperty('script'); }); it('handles audio URL generation failure', async () => { // Mock R2 failure vi.mocked(generateSignedAudioUrl).mockRejectedValue(new Error('R2 error')); const content = await getGatedContent(); expect(content.audioData.error).toBe(true); expect(content.audioData.audioSrc).toBe(''); }); });

Integration Testing

Test the full flow from gate unlock to content rendering:

  • Verify style injection doesn’t conflict with existing styles
  • Ensure script execution doesn’t throw errors
  • Test form submission connection to parent handler
  • Validate audio player initialization

Performance Optimization

Content Caching Strategy

Since the content structure rarely changes, consider caching:

let cachedContent: GatedContentResponse | null = null; let cacheExpiry: number = 0; export async function getGatedContent() { if (cachedContent && Date.now() < cacheExpiry) { // Only regenerate audio URL cachedContent.audioData.audioSrc = await generateSignedAudioUrl(); return cachedContent; } // Generate fresh content const content = await generateFreshContent(); cachedContent = content; cacheExpiry = Date.now() + (5 * 60 * 1000); // 5 min cache return content; }

Lazy Loading Assets

The audio and images can be lazy-loaded:

audioData: { // ...other fields lazyLoad: true, preload: 'metadata' // Only load metadata initially }

Summary

The Gated Content Library serves as the heart of RitoSwap’s exclusive content system, orchestrating a carefully crafted experience for token holders. By combining secure audio delivery, interactive message submission, and immersive styling, it creates a memorable moment that justifies the effort users put into obtaining and using their Colored Key NFTs. The library’s modular design, comprehensive error handling, and security considerations ensure that this exclusive content remains both special and reliable for every user who earns access to it.