From d001cc4ad237536990b618f225f453a24038ca7f Mon Sep 17 00:00:00 2001 From: Ricardo Dinucci Date: Mon, 16 Jun 2025 18:19:40 -0300 Subject: [PATCH 1/3] Add authentication documentation for GenesysGo credentials MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves issue #104 by adding comprehensive documentation explaining how to obtain and use GenesysGo Account UUID and Bearer Token. Includes examples for both React and NodeJS implementations with security best practices. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++ examples/web/README.md | 20 +++++++++++++++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index fbde558..8544399 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,58 @@ const connection = new web3.Connection("{rpc-url}", "confirmed"); const drive = await new ShdwDrive(connection, wallet).init(); ``` +## Authentication + +To use Shadow Drive, you'll need to obtain credentials from GenesysGo: + +### Getting Your GenesysGo Account UUID and Bearer Token + +1. **Account UUID**: This is your unique account identifier provided by GenesysGo. You can obtain this by: + - Contacting GenesysGo support + - Checking your GenesysGo dashboard/account settings + - It's typically provided when you set up your GenesysGo account + +2. **Bearer Token**: This is your authentication token for API access. You can obtain this by: + - Logging into your GenesysGo account + - Navigating to API settings or developer section + - Generating a new API token + +### Using Your Credentials + +Once you have your credentials, replace the placeholders in your code: + +**For React applications** (see `examples/web/src/App.tsx`): +```tsx +const network = "https://us-west-1.genesysgo.net/{YOUR_ACCOUNT_UUID_HERE}"; +// Replace {YOUR_ACCOUNT_UUID_HERE} with your actual UUID + + +``` + +**For NodeJS applications**: +```js +const connection = new web3.Connection( + "https://us-west-1.genesysgo.net/{YOUR_ACCOUNT_UUID_HERE}", + { + commitment: "confirmed", + httpHeaders: { + Authorization: "Bearer {YOUR_BEARER_TOKEN_HERE}", + }, + } +); +``` + +> **Note**: Keep your bearer token secure and never commit it to version control. Consider using environment variables to store sensitive credentials. + ### Examples | package | description | diff --git a/examples/web/README.md b/examples/web/README.md index de85583..c5dbf76 100644 --- a/examples/web/README.md +++ b/examples/web/README.md @@ -1,10 +1,28 @@ # React Shadow Drive Example +## Prerequisites + +Before running this example, you'll need: + +1. **GenesysGo Credentials**: + - Account UUID + - Bearer authentication token + + See the main README for instructions on obtaining these credentials. + +2. **Update App.tsx**: Replace the placeholders in `src/App.tsx`: + ```tsx + // Line 28: Replace with your account UUID + const network = "https://us-west-1.genesysgo.net/YOUR_ACTUAL_UUID_HERE"; + + // Line 46: Replace with your bearer token + Authorization: "Bearer YOUR_ACTUAL_TOKEN_HERE", + ``` + ## Getting Started - From the root directory of this project: - ```bash yarn install cd examples/web From e14850895c3c5cc8a4b17936f43ca1a1b2d9198a Mon Sep 17 00:00:00 2001 From: Ricardo Dinucci Date: Mon, 16 Jun 2025 18:26:17 -0300 Subject: [PATCH 2/3] Fix authentication documentation - clarify RPC vs Shadow Drive auth MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrects the previous commit by properly explaining that: - Shadow Drive uses only Solana wallet signatures (no API keys needed) - UUID/Bearer Token placeholders are for GenesysGo's RPC service (optional) - Users can choose any Solana RPC endpoint - Provides clear options for public RPC vs GenesysGo RPC Resolves issue #104 with accurate technical details. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- README.md | 81 ++++++++++++++++++++++++------------------ examples/web/README.md | 27 +++++++++----- 2 files changed, 65 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 8544399..85eb918 100644 --- a/README.md +++ b/README.md @@ -56,55 +56,68 @@ const drive = await new ShdwDrive(connection, wallet).init(); ## Authentication -To use Shadow Drive, you'll need to obtain credentials from GenesysGo: +Shadow Drive uses **Solana wallet signatures** for authentication. You only need a Solana wallet - no additional API keys or Shadow Drive accounts required. -### Getting Your GenesysGo Account UUID and Bearer Token +### RPC Endpoint Configuration -1. **Account UUID**: This is your unique account identifier provided by GenesysGo. You can obtain this by: - - Contacting GenesysGo support - - Checking your GenesysGo dashboard/account settings - - It's typically provided when you set up your GenesysGo account +You can use any Solana RPC endpoint. The example code shows GenesysGo's RPC service, but this is optional: -2. **Bearer Token**: This is your authentication token for API access. You can obtain this by: - - Logging into your GenesysGo account - - Navigating to API settings or developer section - - Generating a new API token - -### Using Your Credentials - -Once you have your credentials, replace the placeholders in your code: - -**For React applications** (see `examples/web/src/App.tsx`): -```tsx -const network = "https://us-west-1.genesysgo.net/{YOUR_ACCOUNT_UUID_HERE}"; -// Replace {YOUR_ACCOUNT_UUID_HERE} with your actual UUID - - +**Option 1: Public Solana RPC (No additional credentials needed)** +```js +const connection = new web3.Connection("https://api.mainnet-beta.solana.com"); ``` -**For NodeJS applications**: +**Option 2: GenesysGo RPC (Requires their RPC credentials)** ```js const connection = new web3.Connection( - "https://us-west-1.genesysgo.net/{YOUR_ACCOUNT_UUID_HERE}", + "https://us-west-1.genesysgo.net/{YOUR_RPC_ACCOUNT_ID}", { commitment: "confirmed", httpHeaders: { - Authorization: "Bearer {YOUR_BEARER_TOKEN_HERE}", + Authorization: "Bearer {YOUR_RPC_ACCESS_TOKEN}", }, } ); ``` -> **Note**: Keep your bearer token secure and never commit it to version control. Consider using environment variables to store sensitive credentials. +**Option 3: Other RPC providers** +```js +// Helius, QuickNode, Alchemy, etc. +const connection = new web3.Connection("{YOUR_PREFERRED_RPC_URL}"); +``` + +### Shadow Drive Setup + +Regardless of your RPC choice, Shadow Drive setup is the same: + +```js +// Your Solana wallet (browser wallet, keypair, etc.) +const wallet = /* your wallet instance */; + +// Initialize Shadow Drive +const drive = await new ShdwDrive(connection, wallet).init(); +``` + +### Understanding the Example Placeholders + +In `examples/web/src/App.tsx`, the placeholders refer to **GenesysGo's RPC service credentials**: + +- `{YOUR_ACCOUNT_UUID_HERE}` = Your GenesysGo RPC account identifier +- `{GENESYSGO AUTHENTICATION TOKEN HERE}` = Your GenesysGo RPC access token + +**To use the example:** +1. **Option A**: Replace with your GenesysGo RPC credentials +2. **Option B**: Change to a public RPC endpoint and remove the Authorization header + +### How Shadow Drive Authentication Works + +1. **RPC Authentication**: Handled by your connection configuration (varies by provider) +2. **Shadow Drive Authentication**: Automatic via wallet signatures + - No API keys needed + - Uses cryptographic message signing + - Wallet signs authentication messages for each operation + +> **Note**: GenesysGo RPC credentials are for blockchain access only, not Shadow Drive storage. Shadow Drive authenticates through your Solana wallet automatically. ### Examples diff --git a/examples/web/README.md b/examples/web/README.md index c5dbf76..0a9178b 100644 --- a/examples/web/README.md +++ b/examples/web/README.md @@ -4,21 +4,30 @@ Before running this example, you'll need: -1. **GenesysGo Credentials**: - - Account UUID - - Bearer authentication token +1. **Solana Wallet**: Any Solana-compatible wallet (Phantom, Solflare, etc.) + +2. **RPC Endpoint Configuration**: Choose one option for `src/App.tsx`: + + **Option A: Use Public RPC** (No additional setup required) + ```tsx + // Line 28: Replace with public RPC + const network = "https://api.mainnet-beta.solana.com"; - See the main README for instructions on obtaining these credentials. + // Lines 42-48: Remove the httpHeaders config entirely + + ``` -2. **Update App.tsx**: Replace the placeholders in `src/App.tsx`: + **Option B: Use GenesysGo RPC** (Requires GenesysGo RPC credentials) ```tsx - // Line 28: Replace with your account UUID - const network = "https://us-west-1.genesysgo.net/YOUR_ACTUAL_UUID_HERE"; + // Line 28: Replace with your GenesysGo RPC account ID + const network = "https://us-west-1.genesysgo.net/YOUR_RPC_ACCOUNT_ID"; - // Line 46: Replace with your bearer token - Authorization: "Bearer YOUR_ACTUAL_TOKEN_HERE", + // Line 46: Replace with your GenesysGo RPC access token + Authorization: "Bearer YOUR_RPC_ACCESS_TOKEN", ``` + > **Note**: The placeholders in App.tsx are for **RPC access**, not Shadow Drive. Shadow Drive authentication is handled automatically through your connected wallet. + ## Getting Started - From the root directory of this project: From 55c6abf0fb0eaa968a14098f981a1a7400a4069d Mon Sep 17 00:00:00 2001 From: Ricardo Dinucci Date: Mon, 16 Jun 2025 18:40:14 -0300 Subject: [PATCH 3/3] Add max stake button to storage creation interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves issue #106 by adding a "Max Stake" button that: - Displays user's SHDW token balance - Calculates maximum storage size based on available balance - Automatically selects the largest storage tier the user can afford - Updates balance after storage account creation - Provides clear visual feedback with balance chip Features added: - SHDW balance query and display - Max stakeable amount calculation (90% of balance for safety) - Smart storage size selection (1GB/10GB/50GB tiers) - Real-time balance updates - Proper error handling and loading states 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- examples/web/src/Drive.tsx | 91 +++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 1 deletion(-) diff --git a/examples/web/src/Drive.tsx b/examples/web/src/Drive.tsx index e88265e..22dbd94 100644 --- a/examples/web/src/Drive.tsx +++ b/examples/web/src/Drive.tsx @@ -4,7 +4,7 @@ import { useConnection, useWallet } from "@solana/wallet-adapter-react"; import * as anchor from "@project-serum/anchor"; import { WalletMultiButton } from "@solana/wallet-adapter-react-ui"; import { PublicKey } from "@solana/web3.js"; -import { CircularProgress, TextField, FormControl, Select, InputLabel, MenuItem, Button, FormLabel, RadioGroup, FormControlLabel, Radio, styled, LinearProgress, Container, Grid } from "@mui/material"; +import { CircularProgress, TextField, FormControl, Select, InputLabel, MenuItem, Button, FormLabel, RadioGroup, FormControlLabel, Radio, styled, LinearProgress, Container, Grid, Chip } from "@mui/material"; const bytesToHuman = (bytes: any, si = false, dp = 1) => { const thresh = si ? 1024 : 1024; @@ -29,6 +29,39 @@ const bytesToHuman = (bytes: any, si = false, dp = 1) => { return bytes.toFixed(dp) + " " + units[u]; } + +// SHDW token mint address +const SHDW_TOKEN_MINT = new PublicKey("SHDWyBxihqiCj6YekG2GUr7wqKLeLAMK1gHZck9pL6y"); + +// Helper function to find associated token account +async function findAssociatedTokenAddress( + walletAddress: PublicKey, + tokenMintAddress: PublicKey +): Promise { + const [address] = await PublicKey.findProgramAddress( + [ + walletAddress.toBuffer(), + new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA").toBuffer(), + tokenMintAddress.toBuffer(), + ], + new PublicKey("ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL") + ); + return address; +} + +// Calculate maximum storage size based on SHDW balance +// Rough estimate: 1 SHDW ≈ 1GB storage per year +function calculateMaxStorageFromBalance(shdwBalance: number): string { + if (shdwBalance < 0.1) return "1GB"; // Default to minimum + + const maxGB = Math.floor(shdwBalance * 0.9); // Use 90% of balance for safety + + if (maxGB >= 50) return "50GB"; // Max option available + if (maxGB >= 10) return "10GB"; + if (maxGB >= 1) return "1GB"; + + return "1GB"; // Minimum option +} /** * * Simple usage examples for Shadow Drive @@ -51,6 +84,8 @@ export default function Drive() { const [loading, setLoading] = useState(); const [tx, setTx] = useState(); const [version, setVersion] = useState('v2'); + const [shdwBalance, setShdwBalance] = useState(0); + const [balanceLoading, setBalanceLoading] = useState(false); const submitForm = async () => { if (!acc?.publicKey || !fileList) return; try { @@ -100,12 +135,37 @@ export default function Drive() { setLoading(true); const result = await drive?.createStorageAccount(accName, accSize, version); setTx(result!.transaction_signature); + // Refresh balance after creating account + await getUserShdwBalance(); } catch (e) { console.log(e); } refreshAccounts(); setLoading(false); } + + // Get user's SHDW token balance + const getUserShdwBalance = async () => { + if (!wallet.publicKey || !connection) return; + + try { + setBalanceLoading(true); + const userATA = await findAssociatedTokenAddress(wallet.publicKey, SHDW_TOKEN_MINT); + const balance = await connection.getTokenAccountBalance(userATA); + setShdwBalance(balance.value.uiAmount || 0); + } catch (e) { + console.log("Error fetching SHDW balance:", e); + setShdwBalance(0); + } finally { + setBalanceLoading(false); + } + }; + + // Handle "Max Stake" button click + const handleMaxStake = () => { + const maxSize = calculateMaxStorageFromBalance(shdwBalance); + setAccSize(maxSize); + }; useEffect(() => { (async () => { if (wallet) { @@ -121,8 +181,15 @@ export default function Drive() { useEffect(() => { if (drive) { refreshAccounts(); + getUserShdwBalance(); } }, [drive]) + + useEffect(() => { + if (wallet.connected && connection) { + getUserShdwBalance(); + } + }, [wallet.connected]) useEffect(() => { console.log('uploaded'); if (displayFiles) { @@ -146,6 +213,16 @@ export default function Drive() {

Create a Shadow Drive account:

+ + {/* SHDW Balance Display */} +
+ 0 ? "success" : "default"} + variant="outlined" + size="medium" + /> +
50GB + + {/* Max Stake Button */} +