diff --git a/README.md b/README.md
index d880f97..dd722a7 100644
--- a/README.md
+++ b/README.md
@@ -32,6 +32,7 @@ This Turborepo includes the following example applications:
| `@shelby-protocol/cross-chain-accounts` | Shelby cross chain accounts example | [`apps/cross-chain-accounts`](./apps/cross-chain-accounts) |
| `@shelby-protocol/download-example` | An example app to demonstrate downloading blobs using the Shelby SDK | [`apps/download-blob`](./apps/download-blob) |
| `@shelby-protocol/list-example` | An example app to demonstrate listing blobs using the Shelby SDK | [`apps/list-blob`](./apps/list-blob) |
+| `@shelby-protocol/streaming-video` | Shelby Streaming Video example with Gating | [`apps/streaming-video`](./apps/streaming-video) |
| `@shelby-protocol/upload-example` | An example app to demonstrate uploading blobs using the Shelby SDK | [`apps/upload-blob`](./apps/upload-blob) |
diff --git a/apps/solana/token-gated/package.json b/apps/solana/token-gated/package.json
index 1c76114..0e3e582 100644
--- a/apps/solana/token-gated/package.json
+++ b/apps/solana/token-gated/package.json
@@ -26,6 +26,7 @@
"version": "0.0.0",
"private": true,
"dependencies": {
+ "@aptos-labs/ace-sdk": "^0.1.0",
"@coral-xyz/anchor": "^0.31.1",
"@shelby-protocol/react": "latest",
"@shelby-protocol/sdk": "latest",
@@ -35,7 +36,6 @@
"@solana/react-hooks": "^1.1.5",
"@solana/web3.js": "^1.98.4",
"@tanstack/react-query": "^5.90.16",
- "@aptos-labs/ace-sdk": "^0.1.0",
"next": "16.0.10",
"react": "^19.1.0",
"react-dom": "^19.1.0",
@@ -55,4 +55,4 @@
"tailwindcss": "^4",
"typescript": "^5"
}
-}
+}
\ No newline at end of file
diff --git a/apps/streaming-video/.env.example b/apps/streaming-video/.env.example
new file mode 100644
index 0000000..926e2de
--- /dev/null
+++ b/apps/streaming-video/.env.example
@@ -0,0 +1,5 @@
+# Shelby Protocol Configuration
+NEXT_PUBLIC_SHELBY_API_URL=https://api.shelbynet.shelby.xyz
+
+# Shelby API Key for enhanced rate limits
+NEXT_PUBLIC_SHELBY_API_KEY=your_shelby_api_key_here
diff --git a/apps/streaming-video/README.md b/apps/streaming-video/README.md
new file mode 100644
index 0000000..2f89f89
--- /dev/null
+++ b/apps/streaming-video/README.md
@@ -0,0 +1,58 @@
+# Shelby Streaming Video Example
+
+This example demonstrates how to build a video streaming application with decentralized content gating using the Shelby Protocol.
+
+## Key Features
+
+- **Real-time Streaming**: Utilizes Shelby SDK's `streamData` for low-latency video playback.
+- **On-chain Gating**: Protects premium content with rules stored on-chain (Aptos/Solana).
+- **Micropayments**: Integrated "Tip-to-Unlock" flow for content monetization.
+- **Wallet Integration**: Seamless connection with Aptos wallets (Petra, etc.).
+...
+2. **Access Rules**: Creators set a price (e.g., 0.5 APT) and their wallet address receives payments directly.
+
+## Getting Started
+
+### 1. Installation
+
+Install dependencies from the root of the monorepo:
+
+```bash
+pnpm install
+```
+
+### 2. Environment Setup
+
+Copy `.env.example` to `.env` and fill in your API keys:
+
+```bash
+cp .env.example .env
+```
+
+Required variables:
+- `NEXT_PUBLIC_SHELBY_API_KEY`: Your Shelby Protocol API key.
+- `NEXT_PUBLIC_APTOS_API_KEY`: Aptos network API key.
+
+### 3. Development
+
+Run the development server:
+
+```bash
+pnpm dev --filter=@shelby-protocol/streaming-video
+```
+
+Open [http://localhost:3005](http://localhost:3005) to view the app.
+
+## How it Works
+
+1. **Content Upload**: Videos are encrypted and uploaded as blobs to Shelby storage.
+2. **Access Rules**: A access policy is set (e.g., "Must pay 0.1 APT to address X").
+3. **Streaming**: When a user visits the page, the app checks for the payment. If not found, the `VideoPlayer` displays a lock screen.
+4. **Unlocking**: Upon payment, the Shelby SDK provides the authorized stream to the browser.
+
+## Built With
+
+- [Next.js](https://nextjs.org/)
+- [Shelby SDK](https://docs.shelby.xyz)
+- [React Player](https://github.com/cookpete/react-player)
+- [Tailwind CSS](https://tailwindcss.com/)
diff --git a/apps/streaming-video/app/favicon.ico b/apps/streaming-video/app/favicon.ico
new file mode 100644
index 0000000..718d6fe
Binary files /dev/null and b/apps/streaming-video/app/favicon.ico differ
diff --git a/apps/streaming-video/app/fonts/GeistMonoVF.woff b/apps/streaming-video/app/fonts/GeistMonoVF.woff
new file mode 100644
index 0000000..f2ae185
Binary files /dev/null and b/apps/streaming-video/app/fonts/GeistMonoVF.woff differ
diff --git a/apps/streaming-video/app/fonts/GeistVF.woff b/apps/streaming-video/app/fonts/GeistVF.woff
new file mode 100644
index 0000000..1b62daa
Binary files /dev/null and b/apps/streaming-video/app/fonts/GeistVF.woff differ
diff --git a/apps/streaming-video/app/globals.css b/apps/streaming-video/app/globals.css
new file mode 100644
index 0000000..200400d
--- /dev/null
+++ b/apps/streaming-video/app/globals.css
@@ -0,0 +1 @@
+@import "@shelby-protocol/ui/globals.css";
diff --git a/apps/streaming-video/app/layout.tsx b/apps/streaming-video/app/layout.tsx
new file mode 100644
index 0000000..71ecf81
--- /dev/null
+++ b/apps/streaming-video/app/layout.tsx
@@ -0,0 +1,36 @@
+import { Toaster } from "@shelby-protocol/ui/components";
+import localFont from "next/font/local";
+import { WalletProvider } from "@/components/WalletProvider";
+import "./globals.css";
+import type { Metadata } from "next";
+
+const geistSans = localFont({
+ src: "./fonts/GeistVF.woff",
+ variable: "--font-geist-sans",
+});
+const geistMono = localFont({
+ src: "./fonts/GeistMonoVF.woff",
+ variable: "--font-geist-mono",
+});
+
+export const metadata: Metadata = {
+ title: "Shelby Streaming Video Example",
+ description: "A decentralized video streaming example with content gating using Shelby Protocol",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/apps/streaming-video/app/page.tsx b/apps/streaming-video/app/page.tsx
new file mode 100644
index 0000000..a36379a
--- /dev/null
+++ b/apps/streaming-video/app/page.tsx
@@ -0,0 +1,354 @@
+"use client";
+
+import { useState, useRef } from "react";
+import { Header } from "@/components/Header";
+import { VideoPlayer } from "@/components/VideoPlayer";
+import { VideoUploader } from "@/components/VideoUploader";
+import { LoadingSpinner } from "@/components/Loading";
+import { useWallet } from "@aptos-labs/wallet-adapter-react";
+import { toast } from "sonner";
+import { PlayCircle, Upload, ListVideo, User, Trash2, ChevronLeft, ChevronRight } from "lucide-react";
+import { useVideoStorage, VideoMetadata } from "@/hooks/useVideoStorage";
+
+export default function Home() {
+ const { signAndSubmitTransaction, account } = useWallet();
+ const scrollContainerRef = useRef(null);
+
+ // Navigation State
+ const [mainTab, setMainTab] = useState<"watch" | "upload">("watch");
+ const [filterTab, setFilterTab] = useState<"all" | "my">("all");
+
+ // App State
+ const [isLocked, setIsLocked] = useState(true);
+ const [isLoading, setIsLoading] = useState(false);
+ const [loadingText, setLoadingText] = useState("");
+ const [activeVideo, setActiveVideo] = useState(null);
+
+ // Refresh Mechanism
+ const [refreshKey, setRefreshKey] = useState(0);
+ const { videos, removeVideo } = useVideoStorage(refreshKey);
+
+ const handleRefresh = () => {
+ setRefreshKey(prev => prev + 1);
+ };
+
+ const filteredVideos = filterTab === 'all'
+ ? videos
+ : videos.filter(v => account?.address && v.owner === account.address.toString());
+
+ // Carousel Logic
+ const scroll = (direction: 'left' | 'right') => {
+ if (scrollContainerRef.current) {
+ const container = scrollContainerRef.current;
+ const scrollAmount = container.clientWidth * 0.8; // Scroll 80% screen width
+ container.scrollBy({
+ left: direction === 'right' ? scrollAmount : -scrollAmount,
+ behavior: 'smooth'
+ });
+ }
+ };
+
+ const getPlayUrl = (video: VideoMetadata) => {
+ // If video has a direct URL, use it
+ if (video.url) {
+ return video.url;
+ }
+ // If blobName is already a full URL, use it directly
+ if (video.blobName.startsWith('http')) {
+ return video.blobName;
+ }
+ // Otherwise, construct Shelby API URL
+ const owner = video.owner || "0x1";
+ return `https://api.shelbynet.shelby.xyz/shelby/v1/blobs/${owner}/${video.blobName}`;
+ };
+
+ const getVideoTitle = (blobName: string) => {
+ // Remove timestamp suffix (last segment after final hyphen) and format
+ const nameWithoutTimestamp = blobName.replace(/-\d+$/, '');
+ return nameWithoutTimestamp.replace(/_/g, ' ');
+ };
+
+ const handleUnlock = async () => {
+ if (!account) {
+ toast.error("Please connect your wallet first!");
+ return;
+ }
+
+ // Allow owners to view their own content without payment
+ if (activeVideo?.owner === account.address.toString()) {
+ setIsLocked(false);
+ toast.success("Content unlocked! (You own this video)");
+ return;
+ }
+
+ if (!activeVideo?.owner) {
+ toast.error("Video owner information is missing. Cannot process payment.");
+ return;
+ }
+
+ try {
+ setIsLoading(true);
+ setLoadingText("Processing Payment on Aptos...");
+
+ // Use string-based calculation to avoid floating point precision errors
+ const priceFloat = parseFloat(activeVideo.price || "0");
+
+ // Allow free view if price is exactly 0 (for demo videos)
+ if (priceFloat === 0) {
+ setIsLocked(false);
+ toast.success("Demo content unlocked for free!");
+ return;
+ }
+
+ if (isNaN(priceFloat) || priceFloat < 0) {
+ toast.error("Invalid video price.");
+ setIsLoading(false);
+ return;
+ }
+ const amountInOctas = Math.round(priceFloat * 100000000);
+
+ // Ensure the amount is at least 1 octa to prevent free unlocks
+ if (amountInOctas < 1) {
+ toast.error("Price too small. Minimum is 0.00000001 APT.");
+ setIsLoading(false);
+ return;
+ }
+
+ const transactionResponse = await signAndSubmitTransaction({
+ data: {
+ function: "0x1::coin::transfer",
+ typeArguments: ["0x1::aptos_coin::AptosCoin"],
+ functionArguments: [activeVideo.owner, amountInOctas],
+ },
+ });
+
+ console.log("Transaction Hash:", transactionResponse.hash);
+
+ // NOTE: This is a demo implementation. In production, you should:
+ // 1. Wait for transaction confirmation on-chain
+ // 2. Verify the payment was successful and went to the correct address
+ // 3. Only unlock content after verification
+ // Current implementation unlocks immediately after transaction submission
+
+ setIsLocked(false);
+ toast.success("Content Unlocked! Transaction submitted: " + transactionResponse.hash.slice(0, 10) + "...");
+
+ } catch (error: any) {
+ console.error(error);
+ toast.error(error.message || "Payment failed. Please try again.");
+ } finally {
+ setIsLoading(false);
+ setLoadingText(""); // Clear loading text to prevent stale state
+ }
+ };
+
+ const handleSelectVideo = (video: VideoMetadata) => {
+ setActiveVideo(video);
+ setIsLocked(true);
+ setLoadingText(""); // Clear any stale loading text
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ };
+
+ return (
+
+
+ {isLoading &&
}
+
+
+
+ {/* Main Navigation Tabs */}
+
+
+
+
+
+ {/* Content Area */}
+ {mainTab === "watch" ? (
+
+
+ {activeVideo ? (
+ /* Player Mode */
+
+
+
+
+
+
+ ) : (
+ /* Gallery Mode - Carousel Layout */
+ <>
+ {/* Sub-Filters for Gallery */}
+
+
+
+
+
+ {/* Carousel Container */}
+
+
+ {/* Left Arrow */}
+ {filteredVideos.length > 5 && (
+
+ )}
+
+ {/* Scrollable Row */}
+
+ {filteredVideos.map((vid) => {
+ const isOwner = account?.address && vid.owner === account.address.toString();
+
+ return (
+
handleSelectVideo(vid)}
+ className="
+ shrink-0
+ w-full sm:w-[calc(50%-8px)] md:w-[calc(33.33%-11px)] lg:w-[calc(25%-12px)] xl:w-[calc(20%-13px)]
+ group aspect-square bg-black border border-white/10 hover:border-blue-500/50 rounded-2xl overflow-hidden cursor-pointer flex flex-col transition-all duration-300 hover:shadow-2xl hover:shadow-blue-900/10 hover:-translate-y-1
+ "
+ >
+ {/* Top 20%: Actions & Price */}
+
+
+ {isOwner && (
+
+ )}
+
+
+ {vid.price} APT
+
+
+
+ {/* Middle 60%: Video Thumbnail */}
+
+
+
+ {/* Bottom 20%: Info */}
+
+
+ {getVideoTitle(vid.blobName)}
+
+
+
+
+ {vid.owner ? `${vid.owner.slice(0, 4)}...${vid.owner.slice(-4)}` : 'Unknown'}
+
+
+
+
+ );
+ })}
+
+
+ {/* Right Arrow */}
+ {filteredVideos.length > 5 && (
+
+ )}
+
+
+ {filteredVideos.length === 0 && (
+
+
No videos found here
+ {filterTab === 'my' ? (
+
You haven't uploaded any videos yet.
+ ) : (
+
Be the first to upload content!
+ )}
+
+ )}
+ >
+ )}
+
+ ) : (
+
+
+
+ )}
+
+
+
+ );
+}
diff --git a/apps/streaming-video/components.json b/apps/streaming-video/components.json
new file mode 100644
index 0000000..e1847ad
--- /dev/null
+++ b/apps/streaming-video/components.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "../../packages/ui/src/styles/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": true
+ },
+ "iconLibrary": "lucide",
+ "aliases": {
+ "components": "@/components",
+ "hooks": "@/hooks",
+ "lib": "@/lib",
+ "utils": "@shelby-protocol/ui/lib/utils",
+ "ui": "@shelby-protocol/ui/components"
+ }
+}
diff --git a/apps/streaming-video/components/Header.tsx b/apps/streaming-video/components/Header.tsx
new file mode 100644
index 0000000..8be04e4
--- /dev/null
+++ b/apps/streaming-video/components/Header.tsx
@@ -0,0 +1,145 @@
+"use client";
+
+import { useWallet } from "@aptos-labs/wallet-adapter-react";
+import { Button } from "@shelby-protocol/ui/components/button";
+import { useState, useRef, useEffect } from "react";
+import { ChevronDown, LogOut, Copy } from "lucide-react";
+import { toast } from "sonner";
+
+export const Header = () => {
+ const { connected, account, connect, wallets, disconnect } = useWallet();
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false);
+ const dropdownRef = useRef(null);
+
+ // Close dropdown when clicking outside
+ useEffect(() => {
+ const handleClickOutside = (event: MouseEvent) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
+ setIsDropdownOpen(false);
+ }
+ };
+ document.addEventListener("mousedown", handleClickOutside);
+ return () => document.removeEventListener("mousedown", handleClickOutside);
+ }, []);
+
+ const onFaucetAptos = () => {
+ if (!account) return;
+ window.open(
+ `https://docs.shelby.xyz/apis/faucet/aptos?address=${account.address}`,
+ "_blank"
+ );
+ };
+
+ const onMintShelbyUsd = () => {
+ if (!account) return;
+ window.open(
+ `https://docs.shelby.xyz/apis/faucet/shelbyusd?address=${account.address}`,
+ "_blank",
+ );
+ };
+
+ const copyAddress = () => {
+ if (account?.address) {
+ navigator.clipboard.writeText(account.address.toString());
+ toast.success("Address copied!");
+ setIsDropdownOpen(false);
+ }
+ };
+
+ return (
+
+
+
+
+ Shelby Stream
+
+
+
+ {/* Faucet Buttons only visible when connected */}
+ {connected && (
+ <>
+
+
+ >
+ )}
+
+ {/* Custom Wallet Connection UI with Dropdown */}
+ {connected && account ? (
+
+
+
+ {isDropdownOpen && (
+
+
+
+
+
+
+
+ )}
+
+ ) : (
+
+ {wallets.length > 0 && (() => {
+ const preferredWallet = wallets.find(w => w.name.toLowerCase().includes('petra')) || wallets[0];
+ return (
+
+ );
+ })()}
+ {wallets.length === 0 && (
+
+ )}
+
+ )}
+
+
+
+ );
+};
diff --git a/apps/streaming-video/components/Loading.tsx b/apps/streaming-video/components/Loading.tsx
new file mode 100644
index 0000000..1234981
--- /dev/null
+++ b/apps/streaming-video/components/Loading.tsx
@@ -0,0 +1,12 @@
+"use client";
+
+import { Loader2 } from "lucide-react";
+
+export function LoadingSpinner({ text = "Processing..." }: { text?: string }) {
+ return (
+
+ );
+}
diff --git a/apps/streaming-video/components/VideoPlayer.tsx b/apps/streaming-video/components/VideoPlayer.tsx
new file mode 100644
index 0000000..f38eca9
--- /dev/null
+++ b/apps/streaming-video/components/VideoPlayer.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+import { useEffect, useState } from 'react';
+import dynamic from 'next/dynamic';
+
+// Dynamically import ReactPlayer to avoid SSR hydration issues
+const ReactPlayer = dynamic(() => import('react-player'), { ssr: false });
+
+interface VideoPlayerProps {
+ url: string;
+ isLocked: boolean;
+ onUnlock: () => void;
+ poster?: string;
+ loadingText?: string;
+ title?: string;
+ description?: string;
+ price?: string;
+}
+
+export const VideoPlayer = ({ url, isLocked, onUnlock, poster, loadingText, title, description, price }: VideoPlayerProps) => {
+ const [isPlaying, setIsPlaying] = useState(false);
+
+ useEffect(() => {
+ // Auto-play logic when unlocked
+ if (!isLocked) {
+ setIsPlaying(true);
+ } else {
+ setIsPlaying(false);
+ }
+ }, [isLocked]);
+
+ return (
+
+ {isLocked && (
+
+ {loadingText ? (
+
+ ) : (
+
+
{title || "Premium Content"}
+ {description &&
{description}
}
+
+
+
Unlock Price
+
{price || "0.1"} APT
+
+
+
+
+ )}
+
+ )}
+
+
+
console.log("VideoPlayer: Ready to play", url)}
+ onStart={() => console.log("VideoPlayer: Started playing")}
+ onError={(e) => console.error("VideoPlayer: Error playing video", e, url)}
+ playIcon={
+
+ }
+ config={{
+ file: {
+ attributes: {
+ controlsList: 'nodownload', // Disable download button
+ crossOrigin: 'anonymous'
+ },
+ forceVideo: true // Force usage of video tag if extension is missing
+ }
+ }}
+ />
+
+
+ );
+};
diff --git a/apps/streaming-video/components/VideoUploader.tsx b/apps/streaming-video/components/VideoUploader.tsx
new file mode 100644
index 0000000..569f3ac
--- /dev/null
+++ b/apps/streaming-video/components/VideoUploader.tsx
@@ -0,0 +1,170 @@
+"use client";
+
+import { useState, useRef } from "react";
+import { Button } from "@shelby-protocol/ui/components/button";
+import { Input } from "@shelby-protocol/ui/components/input";
+import { Check, Loader2, UploadCloud } from "lucide-react";
+import { useUploadVideo } from "@/hooks/useUploadVideo";
+import { useVideoStorage } from "@/hooks/useVideoStorage";
+import { toast } from "sonner";
+import { useWallet } from "@aptos-labs/wallet-adapter-react";
+
+export const VideoUploader = ({ onSuccess }: { onSuccess?: () => void }) => {
+ const [file, setFile] = useState(null);
+ const [price, setPrice] = useState("0.1");
+ const [description, setDescription] = useState("");
+ const [uploadedBlobName, setUploadedBlobName] = useState(null);
+ const fileInputRef = useRef(null);
+
+ const { uploadVideo, isUploading } = useUploadVideo();
+ const { addVideo } = useVideoStorage();
+ const { account } = useWallet();
+
+ const handleFileChange = (e: React.ChangeEvent) => {
+ if (e.target.files && e.target.files[0]) {
+ setFile(e.target.files[0]);
+ setUploadedBlobName(null);
+ }
+ };
+
+ const handleUpload = async () => {
+ if (!file) {
+ toast.error("Please select a video file first.");
+ return;
+ }
+
+ // Capture values now to avoid race conditions if user edits inputs during upload
+ const capturedPrice = price;
+ const capturedDescription = description || "Uploaded Video";
+
+ // Validate price
+ const priceFloat = parseFloat(capturedPrice);
+ if (isNaN(priceFloat) || priceFloat <= 0) {
+ toast.error("Please enter a valid price greater than 0.");
+ return;
+ }
+
+ // Check for minimum octa amount (matching page.tsx)
+ const amountInOctas = Math.round(priceFloat * 100000000);
+ if (amountInOctas < 1) {
+ toast.error("Price too small. Minimum is 0.00000001 APT.");
+ return;
+ }
+
+ try {
+ const result = await uploadVideo(file);
+ if (result && result.blobName) {
+ setUploadedBlobName(result.blobName);
+
+ if (account?.address) {
+ addVideo(result.blobName, capturedDescription, capturedPrice, account.address.toString());
+ }
+
+ if (onSuccess) {
+ onSuccess();
+ }
+
+ // Reset form
+ setFile(null);
+ setDescription("");
+ if (fileInputRef.current) fileInputRef.current.value = "";
+ toast.success("Video uploaded successfully!");
+ }
+ } catch (e) {
+ console.error(e);
+ }
+ };
+
+ return (
+
+
+
+ Upload New Video
+
+
+
+
fileInputRef.current?.click()}
+ >
+
+ {file ? (
+
+ Selected: {file.name} ({(file.size / (1024 * 1024)).toFixed(2)} MB)
+
+ ) : (
+
+
+
Click to select a video file (MP4, WebM)
+
+ )}
+
+
+ {uploadedBlobName && (
+
+
+
+
+
+
Video Uploaded Successfully!
+
Ready to stream on ShelbyNet
+
+
+
+ )}
+
+
+
+
+
+
+ );
+};
diff --git a/apps/streaming-video/components/WalletProvider.tsx b/apps/streaming-video/components/WalletProvider.tsx
new file mode 100644
index 0000000..d33ac41
--- /dev/null
+++ b/apps/streaming-video/components/WalletProvider.tsx
@@ -0,0 +1,29 @@
+"use client";
+
+import { setupAutomaticEthereumWalletDerivation } from "@aptos-labs/derived-wallet-ethereum";
+import { setupAutomaticSolanaWalletDerivation } from "@aptos-labs/derived-wallet-solana";
+import { Network } from "@aptos-labs/ts-sdk";
+import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react";
+import type { PropsWithChildren } from "react";
+
+setupAutomaticSolanaWalletDerivation({ defaultNetwork: Network.SHELBYNET });
+setupAutomaticEthereumWalletDerivation({ defaultNetwork: Network.SHELBYNET });
+
+export const WalletProvider = ({ children }: PropsWithChildren) => {
+ return (
+ {
+ console.log("error", error);
+ }}
+ >
+ {children}
+
+ );
+};
diff --git a/apps/streaming-video/hooks/useUploadVideo.tsx b/apps/streaming-video/hooks/useUploadVideo.tsx
new file mode 100644
index 0000000..9529943
--- /dev/null
+++ b/apps/streaming-video/hooks/useUploadVideo.tsx
@@ -0,0 +1,96 @@
+import { useState } from "react";
+import { getShelbyClient } from "@/utils/client";
+import { useWallet } from "@aptos-labs/wallet-adapter-react";
+import { toast } from "sonner";
+import {
+ ClayErasureCodingProvider,
+ expectedTotalChunksets,
+ generateCommitments,
+} from "@shelby-protocol/sdk/browser";
+
+export const useUploadVideo = () => {
+ const { signAndSubmitTransaction, account } = useWallet();
+ const [isUploading, setIsUploading] = useState(false);
+
+ const uploadVideo = async (file: File) => {
+ setIsUploading(true);
+ try {
+ if (!account) {
+ toast.error("Please connect your wallet first!");
+ throw new Error("Wallet not connected");
+ }
+
+ const client = getShelbyClient();
+
+ // DẪN CHỨNG: Trên Shelbynet, module nằm tại địa chỉ này
+ const moduleAddress = "0xc63d6a5efb0080a6029403131715bd4971e1149f7cc099aac69bb0069b3ddbf5";
+
+ const arrayBuffer = await file.arrayBuffer();
+ const buffer = Buffer.from(arrayBuffer);
+ const blobName = `${file.name.replace(/[^a-zA-Z0-9.-]/g, "_")}-${Date.now()}`;
+
+ const provider = await ClayErasureCodingProvider.create();
+ const commitments = await generateCommitments(provider, buffer);
+
+ // CHUYỂN ĐỔI: Sử dụng Uint8Array cho vector (Browser compatible)
+ const cleanHex = commitments.blob_merkle_root.startsWith("0x")
+ ? commitments.blob_merkle_root.slice(2)
+ : commitments.blob_merkle_root;
+ const merkleRootBytes = new Uint8Array(
+ cleanHex.match(/.{1,2}/g)?.map((byte) => parseInt(byte, 16)) || []
+ );
+
+ // Tính thời gian hết hạn: Đặt time-to-live là 30 ngày (tính bằng microseconds)
+ const TTL_SECONDS = 30 * 24 * 60 * 60; // 30 days
+ const expirationMicros = BigInt(Date.now() * 1000) + BigInt(TTL_SECONDS * 1000000);
+
+ // Log để debug
+ console.log("Debug Upload:", {
+ moduleAddress,
+ blobName,
+ expirationMicros: expirationMicros.toString(),
+ nowMicros: BigInt(Date.now() * 1000).toString()
+ });
+
+ const payload = {
+ data: {
+ function: `${moduleAddress}::blob_metadata::register_blob`,
+ typeArguments: [],
+ functionArguments: [
+ blobName, // 1. String
+ expirationMicros.toString(), // 2. u64 (Expiration)
+ merkleRootBytes, // 3. vector (Uint8Array)
+ expectedTotalChunksets(commitments.raw_data_size), // 4. u32 (Number)
+ String(commitments.raw_data_size), // 5. u64 (Size)
+ 0, // 6. u8 (Number)
+ 0 // 7. u8 (Number)
+ ],
+ }
+ };
+
+ // Gửi giao dịch
+ const response = await signAndSubmitTransaction(payload as any);
+
+ // Upload Blob Data lên RPC
+ await client.rpc.putBlob({
+ account: account.address,
+ blobName,
+ blobData: new Uint8Array(buffer),
+ });
+
+ // Success toast is shown by VideoUploader component
+ return { blobName, transactionHash: response.hash };
+
+ } catch (error: any) {
+ console.error("Execution error:", error);
+ // In chi tiết lỗi từ Simulation nếu có
+ const errorMsg = error.data?.message || error.message || "Transaction failed";
+ toast.error(errorMsg);
+ throw error;
+ } finally {
+ setIsUploading(false);
+ }
+ };
+
+ return { uploadVideo, isUploading };
+};
\ No newline at end of file
diff --git a/apps/streaming-video/hooks/useVideoStorage.ts b/apps/streaming-video/hooks/useVideoStorage.ts
new file mode 100644
index 0000000..60ee997
--- /dev/null
+++ b/apps/streaming-video/hooks/useVideoStorage.ts
@@ -0,0 +1,105 @@
+import { useState, useEffect } from 'react';
+
+export interface VideoMetadata {
+ blobName: string;
+ description: string;
+ price: string;
+ timestamp: number;
+ owner?: string;
+ url?: string; // Optional: full URL for external videos
+}
+
+const DEFAULT_VIDEOS: VideoMetadata[] = [
+ {
+ blobName: "big_buck_bunny",
+ description: "Big Buck Bunny - Open Source Animation",
+ price: "0",
+ timestamp: Date.now(),
+ owner: "0x1",
+ url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
+ },
+ {
+ blobName: "sintel",
+ description: "Sintel - Fantasy Short Film",
+ price: "0",
+ timestamp: Date.now() - 100000,
+ owner: "0x1",
+ url: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/Sintel.mp4"
+ }
+];
+
+export const useVideoStorage = (trigger?: number) => {
+ const [videos, setVideos] = useState([]);
+
+ useEffect(() => {
+ loadVideos();
+ }, [trigger]); // Re-load when trigger changes, or on mount
+
+ const loadVideos = () => {
+ try {
+ const stored = localStorage.getItem('shelby_videos_v2');
+ if (stored) {
+ const parsed = JSON.parse(stored);
+ if (Array.isArray(parsed)) {
+ setVideos(parsed);
+ } else {
+ // Invalid format, reset and clean up
+ setVideos(DEFAULT_VIDEOS);
+ localStorage.setItem('shelby_videos_v2', JSON.stringify(DEFAULT_VIDEOS));
+ }
+ } else {
+ setVideos(DEFAULT_VIDEOS);
+ localStorage.setItem('shelby_videos_v2', JSON.stringify(DEFAULT_VIDEOS));
+ }
+ } catch (e) {
+ console.error("Storage error:", e);
+ setVideos(DEFAULT_VIDEOS);
+ localStorage.setItem('shelby_videos_v2', JSON.stringify(DEFAULT_VIDEOS));
+ }
+ };
+
+ const addVideo = (blobName: string, description: string, price: string, owner: string) => {
+ const newVideo: VideoMetadata = {
+ blobName,
+ description,
+ price,
+ owner,
+ timestamp: Date.now()
+ };
+
+ try {
+ // Always read fresh state from localStorage to ensure consistency
+ const currentStored = localStorage.getItem('shelby_videos_v2');
+ let currentVideos = currentStored ? JSON.parse(currentStored) : DEFAULT_VIDEOS;
+ if (!Array.isArray(currentVideos)) currentVideos = DEFAULT_VIDEOS;
+
+ const updatedVideos = [newVideo, ...currentVideos];
+ setVideos(updatedVideos);
+ localStorage.setItem('shelby_videos_v2', JSON.stringify(updatedVideos));
+ } catch (e) {
+ console.error("Storage error in addVideo:", e);
+ // Fallback: just add to current state
+ const updatedVideos = [newVideo, ...videos];
+ setVideos(updatedVideos);
+ }
+ };
+
+ const removeVideo = (blobName: string) => {
+ try {
+ const currentStored = localStorage.getItem('shelby_videos_v2');
+ let currentVideos: VideoMetadata[] = currentStored ? JSON.parse(currentStored) : videos;
+ if (!Array.isArray(currentVideos)) currentVideos = DEFAULT_VIDEOS;
+
+ const updatedVideos = currentVideos.filter(v => v.blobName !== blobName);
+ setVideos(updatedVideos);
+ localStorage.setItem('shelby_videos_v2', JSON.stringify(updatedVideos));
+ } catch (e) {
+ console.error("Storage error in removeVideo:", e);
+ // Fallback: filter from current state
+ const updatedVideos = videos.filter(v => v.blobName !== blobName);
+ setVideos(updatedVideos);
+ }
+ };
+
+ return { videos, addVideo, removeVideo };
+};
diff --git a/apps/streaming-video/next-env.d.ts b/apps/streaming-video/next-env.d.ts
new file mode 100644
index 0000000..830fb59
--- /dev/null
+++ b/apps/streaming-video/next-env.d.ts
@@ -0,0 +1,6 @@
+///
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/apps/streaming-video/next.config.ts b/apps/streaming-video/next.config.ts
new file mode 100644
index 0000000..baf3206
--- /dev/null
+++ b/apps/streaming-video/next.config.ts
@@ -0,0 +1,26 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ transpilePackages: ["@shelby-protocol/ui"],
+ typedRoutes: true,
+ images: {
+ remotePatterns: [
+ {
+ protocol: "https",
+ hostname: "api.shelbynet.shelby.xyz",
+ port: "",
+ pathname: "/**",
+ },
+ ],
+ },
+ async rewrites() {
+ return [
+ {
+ source: "/api/shelby/:path*",
+ destination: "https://api.shelbynet.shelby.xyz/shelby/v1/:path*",
+ },
+ ];
+ },
+};
+
+export default nextConfig;
diff --git a/apps/streaming-video/package.json b/apps/streaming-video/package.json
new file mode 100644
index 0000000..aa8d5e9
--- /dev/null
+++ b/apps/streaming-video/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@shelby-protocol/streaming-video",
+ "version": "0.0.0",
+ "description": "Shelby Streaming Video example with Gating",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --turbopack --port 3005",
+ "build": "next build",
+ "start": "next start",
+ "lint": "biome check .",
+ "fmt": "biome check . --write"
+ },
+ "dependencies": {
+ "@aptos-labs/derived-wallet-ethereum": "^0.8.5",
+ "@aptos-labs/derived-wallet-solana": "^0.10.0",
+ "@aptos-labs/ts-sdk": "^5.1.1",
+ "@aptos-labs/wallet-adapter-react": "^8.1.0",
+ "@shelby-protocol/sdk": "latest",
+ "@shelby-protocol/ui": "workspace:*",
+ "lucide-react": "^0.378.0",
+ "next": "^15.5.7",
+ "react": "^19.1.0",
+ "react-dom": "^19.1.0",
+ "react-player": "^2.16.1",
+ "sonner": "^1.4.41"
+ },
+ "devDependencies": {
+ "@types/node": "^22.15.3",
+ "@types/react": "^18.3.12",
+ "@types/react-dom": "^18.3.1"
+ }
+}
\ No newline at end of file
diff --git a/apps/streaming-video/postcss.config.mjs b/apps/streaming-video/postcss.config.mjs
new file mode 100644
index 0000000..e4b5ceb
--- /dev/null
+++ b/apps/streaming-video/postcss.config.mjs
@@ -0,0 +1 @@
+export { default } from "@shelby-protocol/ui/postcss.config";
diff --git a/apps/streaming-video/tsconfig.json b/apps/streaming-video/tsconfig.json
new file mode 100644
index 0000000..1e70252
--- /dev/null
+++ b/apps/streaming-video/tsconfig.json
@@ -0,0 +1,28 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./*"],
+ "@shelby-protocol/ui/*": ["../../packages/ui/src/*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/apps/streaming-video/utils/client.ts b/apps/streaming-video/utils/client.ts
new file mode 100644
index 0000000..1adb005
--- /dev/null
+++ b/apps/streaming-video/utils/client.ts
@@ -0,0 +1,31 @@
+import { Aptos, AptosConfig, Network } from "@aptos-labs/ts-sdk";
+import { ShelbyClient } from "@shelby-protocol/sdk/browser";
+
+let aptosClient: Aptos | undefined;
+let shelbyClient: ShelbyClient | undefined;
+
+export const getAptosClient = () => {
+ if (!aptosClient) {
+ aptosClient = new Aptos(
+ new AptosConfig({
+ network: Network.CUSTOM,
+ fullnode: typeof window !== "undefined" ? "/api/shelby" : "https://api.shelbynet.shelby.xyz/v1",
+ clientConfig: {
+ API_KEY: process.env.NEXT_PUBLIC_APTOS_API_KEY,
+ },
+ }),
+ );
+ }
+ return aptosClient;
+};
+
+export const getShelbyClient = () => {
+ if (!shelbyClient) {
+ shelbyClient = new ShelbyClient({
+ network: Network.SHELBYNET,
+ // Only use API key if it exists, otherwise rely on public access for reads
+ apiKey: process.env.NEXT_PUBLIC_SHELBY_API_KEY || "",
+ });
+ }
+ return shelbyClient;
+};
diff --git a/package.json b/package.json
index 6a22f03..d9fb60b 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,8 @@
},
"pnpm": {
"overrides": {
- "keyv": "npm:@keyvhq/core@~1.6.26"
+ "keyv": "npm:@keyvhq/core@~1.6.26",
+ "@aptos-labs/ace-sdk": "npm:noop@latest"
},
"onlyBuiltDependencies": [
"@biomejs/biome",
@@ -31,4 +32,4 @@
"sharp"
]
}
-}
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2153ecd..200ab81 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -6,6 +6,7 @@ settings:
overrides:
keyv: npm:@keyvhq/core@~1.6.26
+ '@aptos-labs/ace-sdk': npm:noop@latest
importers:
@@ -58,7 +59,7 @@ importers:
version: 3.3.2
openai:
specifier: ^6.6.0
- version: 6.16.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@4.3.5)
+ version: 6.16.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@4.3.5)
react:
specifier: ^19.1.0
version: 19.2.3
@@ -162,9 +163,6 @@ importers:
apps/solana/token-gated:
dependencies:
- '@aptos-labs/ace-sdk':
- specifier: ^0.1.0
- version: 0.1.0(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))
'@coral-xyz/anchor':
specifier: ^0.31.1
version: 0.31.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
@@ -176,7 +174,7 @@ importers:
version: 0.0.9(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))
'@shelby-protocol/solana-kit':
specifier: latest
- version: 0.1.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(got@11.8.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)
+ version: 0.1.2(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(got@11.8.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)
'@solana/client':
specifier: ^1.2.0
version: 1.6.0(@solana/sysvars@5.4.0(typescript@5.9.3))(@types/react@19.1.0)(bufferutil@4.1.0)(react@19.2.3)(typescript@5.9.3)(use-sync-external-store@1.6.0(react@19.2.3))(utf-8-validate@5.0.10)
@@ -242,6 +240,67 @@ importers:
specifier: ^5
version: 5.9.3
+ apps/streaming-video:
+ dependencies:
+ '@aptos-labs/derived-wallet-ethereum':
+ specifier: ^0.8.5
+ version: 0.8.5(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5)
+ '@aptos-labs/derived-wallet-solana':
+ specifier: ^0.10.0
+ version: 0.10.0(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+ '@aptos-labs/ts-sdk':
+ specifier: ^5.1.1
+ version: 5.2.1(got@11.8.6)
+ '@aptos-labs/wallet-adapter-react':
+ specifier: ^8.1.0
+ version: 8.1.0(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@telegram-apps/bridge@1.9.2)(@types/react@18.3.27)(@wallet-standard/core@1.1.1)(react@19.2.3)
+ '@shelby-protocol/sdk':
+ specifier: latest
+ version: 0.0.9(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))
+ '@shelby-protocol/ui':
+ specifier: workspace:*
+ version: link:../../packages/ui
+ dotenv:
+ specifier: ^16.6.1
+ version: 16.6.1
+ form-data:
+ specifier: ^4.0.4
+ version: 4.0.5
+ lucide-react:
+ specifier: ^0.378.0
+ version: 0.378.0(react@19.2.3)
+ next:
+ specifier: ^15.5.7
+ version: 15.5.9(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ node-fetch:
+ specifier: ^3.3.2
+ version: 3.3.2
+ openai:
+ specifier: ^6.6.0
+ version: 6.16.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@4.3.5)
+ react:
+ specifier: ^19.1.0
+ version: 19.2.3
+ react-dom:
+ specifier: ^19.1.0
+ version: 19.2.3(react@19.2.3)
+ react-player:
+ specifier: ^2.16.1
+ version: 2.16.1(react@19.2.3)
+ sonner:
+ specifier: ^1.4.41
+ version: 1.7.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3)
+ devDependencies:
+ '@types/node':
+ specifier: ^22.15.3
+ version: 22.19.6
+ '@types/react':
+ specifier: ^18.3.12
+ version: 18.3.27
+ '@types/react-dom':
+ specifier: ^18.3.1
+ version: 18.3.7(@types/react@18.3.27)
+
apps/upload-blob:
dependencies:
'@aptos-labs/ts-sdk':
@@ -385,12 +444,6 @@ packages:
'@aptos-labs/wallet-standard': ^0.5.0
'@telegram-apps/bridge': ^1.0.0
- '@aptos-labs/ace-sdk@0.1.0':
- resolution: {integrity: sha512-wp3gwgmELU20cF7IWiEx8vVKh8GKK42D0YI+nAf+jBk7b/tYyModMN+wc+/UZQljJdYR+eT4aZmHAN++L/zGuQ==}
- peerDependencies:
- '@aptos-labs/ts-sdk': ^5.2.0
- '@solana/web3.js': ^1.95.0
-
'@aptos-labs/aptos-cli@1.1.1':
resolution: {integrity: sha512-sB7CokCM6s76SLJmccysbnFR+MDik6udKfj2+9ZsmTLV0/t73veIeCDKbvWJmbW267ibx4HiGbPI7L+1+yjEbQ==}
hasBin: true
@@ -406,6 +459,11 @@ packages:
peerDependencies:
'@aptos-labs/ts-sdk': ^5.1.1
+ '@aptos-labs/derived-wallet-base@0.11.0':
+ resolution: {integrity: sha512-RoI6bWbRQIAkzm+rd9AtgPZ0/jmmYyyTwYtVqjMyzEOIRD9nfMjOKzixVtD+y6H1O/QXpAD4HSTJF4mitLueaQ==}
+ peerDependencies:
+ '@aptos-labs/ts-sdk': ^5.1.1
+
'@aptos-labs/derived-wallet-ethereum@0.8.5':
resolution: {integrity: sha512-mV9cNOzfVSLr8HYMJ1UTrsxGgET/awou+QFY55jtydXD9efaGB3mCTi65Y4oQU8fe4TyF0NNFG9HTnzaZH3RLA==}
peerDependencies:
@@ -416,8 +474,8 @@ packages:
peerDependencies:
'@aptos-labs/ts-sdk': ^5.1.1
- '@aptos-labs/derived-wallet-solana@0.9.2':
- resolution: {integrity: sha512-jUG+REJ72+f5WmirUiqyiiyKwQSvp+TqOWvrcKkSY4oygzGGpZGXkIrOVm53lA9/iwRk8RbQXHO/pakUCfjSMw==}
+ '@aptos-labs/derived-wallet-solana@0.12.0':
+ resolution: {integrity: sha512-9jco25sSBPdYvda8uC3HyFDVMKKzhVrgqbTnEoDelevUm5JG9ueFXJBbBDRu6QQA/OZBP/xlfWDY4VvrBTFeaQ==}
peerDependencies:
'@aptos-labs/ts-sdk': ^5.1.1
@@ -1716,12 +1774,16 @@ packages:
peerDependencies:
'@aptos-labs/ts-sdk': ^2.0.1 || ^3.0.0 || ^4.0.0 || ^5.0.0
- '@shelby-protocol/solana-kit@0.1.1':
- resolution: {integrity: sha512-qWEjvdH8QSgrmmGZ/s9QWear6XxmzdZKPDpTNBjUbrI9Ug67xAwcgCW9S+NrSXcL1r6y6HBxMzjSeRz9NNfIGw==}
+ '@shelby-protocol/solana-kit@0.1.2':
+ resolution: {integrity: sha512-CUsjP4R9LbUhiJyzxtgNeFo/740+PzZDqsupQLnQn1SDbD0vVu2I46u1SogpCqXnW2g/x8E4zYuj4CgRJS37WA==}
peerDependencies:
- '@solana/web3.js': ^1.98.4
react: ^18.0.0 || ^19.0.0
react-dom: ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-dom:
+ optional: true
'@sindresorhus/is@4.6.0':
resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==}
@@ -2907,6 +2969,10 @@ packages:
deep-is@0.1.4:
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
defer-to-connect@2.0.1:
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
engines: {node: '>=10'}
@@ -3675,6 +3741,9 @@ packages:
resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==}
engines: {node: '>= 12.0.0'}
+ load-script@1.0.0:
+ resolution: {integrity: sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA==}
+
locate-path@6.0.0:
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
engines: {node: '>=10'}
@@ -3696,6 +3765,11 @@ packages:
lru-cache@5.1.1:
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+ lucide-react@0.378.0:
+ resolution: {integrity: sha512-u6EPU8juLUk9ytRcyapkWI18epAv3RU+6+TC23ivjR0e+glWKBobFeSgRwOIJihzktILQuy6E0E80P2jVTDR5g==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0
+
lucide-react@0.544.0:
resolution: {integrity: sha512-t5tS44bqd825zAW45UQxpG2CvcC4urOwn2TrwSH8u+MjeE+1NnWl6QqeQ/6NdjMqdOygyiT9p3Ev0p1NJykxjw==}
peerDependencies:
@@ -3708,6 +3782,9 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ memoize-one@5.2.1:
+ resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==}
+
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -4038,9 +4115,17 @@ packages:
peerDependencies:
react: ^19.2.3
+ react-fast-compare@3.2.2:
+ resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==}
+
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ react-player@2.16.1:
+ resolution: {integrity: sha512-mxP6CqjSWjidtyDoMOSHVPdhX0pY16aSvw5fVr44EMaT7X5Xz46uQ4b/YBm1v2x+3hHkB9PmjEEkmbHb9PXQ4w==}
+ peerDependencies:
+ react: '>=16.6.0'
+
react-remove-scroll-bar@2.3.8:
resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==}
engines: {node: '>=10'}
@@ -4202,6 +4287,12 @@ packages:
sisteransi@1.0.5:
resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+ sonner@1.7.4:
+ resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==}
+ peerDependencies:
+ react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+ react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc
+
sonner@2.0.7:
resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==}
peerDependencies:
@@ -4753,14 +4844,6 @@ snapshots:
transitivePeerDependencies:
- '@wallet-standard/core'
- '@aptos-labs/ace-sdk@0.1.0(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))':
- dependencies:
- '@aptos-labs/ts-sdk': 5.2.1(got@11.8.6)
- '@noble/ciphers': 1.3.0
- '@noble/curves': 1.9.7
- '@noble/hashes': 1.8.0
- '@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
-
'@aptos-labs/aptos-cli@1.1.1':
dependencies:
commander: 12.1.0
@@ -4776,6 +4859,13 @@ snapshots:
transitivePeerDependencies:
- '@wallet-standard/core'
+ '@aptos-labs/derived-wallet-base@0.11.0(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)':
+ dependencies:
+ '@aptos-labs/ts-sdk': 5.2.1(got@11.8.6)
+ '@aptos-labs/wallet-standard': 0.5.2(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)
+ transitivePeerDependencies:
+ - '@wallet-standard/core'
+
'@aptos-labs/derived-wallet-ethereum@0.8.5(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5)':
dependencies:
'@aptos-labs/derived-wallet-base': 0.10.1(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)
@@ -4810,9 +4900,9 @@ snapshots:
- typescript
- utf-8-validate
- '@aptos-labs/derived-wallet-solana@0.9.2(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)':
+ '@aptos-labs/derived-wallet-solana@0.12.0(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)':
dependencies:
- '@aptos-labs/derived-wallet-base': 0.10.1(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)
+ '@aptos-labs/derived-wallet-base': 0.11.0(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)
'@aptos-labs/ts-sdk': 5.2.1(got@11.8.6)
'@aptos-labs/wallet-standard': 0.5.2(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)
'@solana/wallet-adapter-base': 0.9.27(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))
@@ -4820,6 +4910,7 @@ snapshots:
'@solana/wallet-standard-wallet-adapter-base': 1.1.4(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(bs58@6.0.0)
'@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
'@wallet-standard/app': 1.1.0
+ tweetnacl: 1.0.3
transitivePeerDependencies:
- '@wallet-standard/core'
- bs58
@@ -5993,18 +6084,18 @@ snapshots:
p-limit: 7.1.1
zod: 3.25.76
- '@shelby-protocol/solana-kit@0.1.1(@solana/web3.js@1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10))(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(got@11.8.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)':
+ '@shelby-protocol/solana-kit@0.1.2(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(got@11.8.6)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(typescript@5.9.3)(utf-8-validate@5.0.10)':
dependencies:
- '@aptos-labs/derived-wallet-base': 0.10.1(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)
- '@aptos-labs/derived-wallet-solana': 0.9.2(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+ '@aptos-labs/derived-wallet-solana': 0.12.0(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)(bs58@6.0.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
'@aptos-labs/gas-station-client': 2.0.3(got@11.8.6)
'@aptos-labs/ts-sdk': 5.2.1(got@11.8.6)
+ '@aptos-labs/wallet-standard': 0.5.2(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))(@wallet-standard/core@1.1.1)
'@shelby-protocol/sdk': 0.0.9(@aptos-labs/ts-sdk@5.2.1(got@11.8.6))
- '@solana/wallet-standard-util': 1.1.2
+ '@solana/wallet-standard-features': 1.3.0
'@solana/web3.js': 1.98.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@5.0.10)
+ optionalDependencies:
react: 19.2.3
react-dom: 19.2.3(react@19.2.3)
- tweetnacl: 1.0.3
transitivePeerDependencies:
- '@wallet-standard/core'
- bs58
@@ -7352,6 +7443,8 @@ snapshots:
deep-is@0.1.4: {}
+ deepmerge@4.3.1: {}
+
defer-to-connect@2.0.1: {}
define-data-property@1.1.4:
@@ -8272,6 +8365,8 @@ snapshots:
lightningcss-win32-arm64-msvc: 1.30.2
lightningcss-win32-x64-msvc: 1.30.2
+ load-script@1.0.0: {}
+
locate-path@6.0.0:
dependencies:
p-locate: 5.0.0
@@ -8290,6 +8385,10 @@ snapshots:
dependencies:
yallist: 3.1.1
+ lucide-react@0.378.0(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+
lucide-react@0.544.0(react@19.2.3):
dependencies:
react: 19.2.3
@@ -8300,6 +8399,8 @@ snapshots:
math-intrinsics@1.1.0: {}
+ memoize-one@5.2.1: {}
+
merge2@1.4.1: {}
micromatch@4.0.8:
@@ -8469,9 +8570,9 @@ snapshots:
dependencies:
wrappy: 1.0.2
- openai@6.16.0(ws@8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@4.3.5):
+ openai@6.16.0(ws@8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10))(zod@4.3.5):
optionalDependencies:
- ws: 8.18.3(bufferutil@4.1.0)(utf-8-validate@5.0.10)
+ ws: 8.19.0(bufferutil@4.1.0)(utf-8-validate@5.0.10)
zod: 4.3.5
optionator@0.9.4:
@@ -8595,8 +8696,19 @@ snapshots:
react: 19.2.3
scheduler: 0.27.0
+ react-fast-compare@3.2.2: {}
+
react-is@16.13.1: {}
+ react-player@2.16.1(react@19.2.3):
+ dependencies:
+ deepmerge: 4.3.1
+ load-script: 1.0.0
+ memoize-one: 5.2.1
+ prop-types: 15.8.1
+ react: 19.2.3
+ react-fast-compare: 3.2.2
+
react-remove-scroll-bar@2.3.8(@types/react@18.3.27)(react@19.2.3):
dependencies:
react: 19.2.3
@@ -8843,6 +8955,11 @@ snapshots:
sisteransi@1.0.5: {}
+ sonner@1.7.4(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
+ dependencies:
+ react: 19.2.3
+ react-dom: 19.2.3(react@19.2.3)
+
sonner@2.0.7(react-dom@19.2.3(react@19.2.3))(react@19.2.3):
dependencies:
react: 19.2.3