Skip to content

shelby/solana-starter

Repository files navigation

Shelby Protocol × Solana Starter

A quickstart demo showing how to upload blobs to Shelby Protocol using your Solana wallet.

Next.js TypeScript Solana Shelby

Demo

What is Shelby?

Shelby is a chain-agnostic decentralized file storage protocol. Think of it as read-optimized blob storage that works across blockchains:

  • Store from any chain: Sign transactions with your existing wallet (Solana, Aptos, EVM)
  • Retrieve via HTTP: Simple GET requests to fetch your data—no SDK required
  • Performant: Optimized for read-heavy workloads

Learn more at shelby.xyz

Quick Start

Prerequisites

  • Node.js 18+
  • pnpm, npm, or yarn
  • A Solana wallet browser extension (Solflare, Phantom, etc.)

Setup

# Clone the repo
git clone git@github.com:shelby/solana-starter.git
cd solana-starter

# Install dependencies
pnpm install

# Copy environment variables
cp .env.example .env.local

# Start the dev server
pnpm dev

Open http://localhost:3000 in your browser.

Environment Variables

Variable Description
NEXT_PUBLIC_SHELBYNET_API_KEY Shelby API key
NEXT_PUBLIC_SOLANA_RPC (Optional) Solana RPC endpoint, defaults to Devnet

NOTE: Get your free API key from Geomi. See this video for a walkthrough.

Easy as 1, 2, 3

This demo walks through the complete flow for storing blobs on Shelby using a Solana wallet.

Step 1: Connect Wallet

When you connect your Solana wallet, a storage account is automatically derived from your wallet address. This storage account is your identity on Shelby.

Key code:

src/utils/shelbyClient.ts:10-13

export const shelbyClient = new ShelbyClient({
  network: Network.SHELBYNET,
  apiKey: process.env.NEXT_PUBLIC_SHELBYNET_API_KEY || "",
});

src/components/WalletProvider.tsx:14-18

const client = createClient({
  endpoint:
    process.env.NEXT_PUBLIC_SOLANA_RPC || "https://api.devnet.solana.com",
  walletConnectors: autoDiscover({ filter: isSolanaWallet }),
});

src/components/Header.tsx:19-20

const { connectors, connect, disconnect, wallet, status } =
  useWalletConnection();

Step 2: Fund Account

The derived storage account needs ShelbyUSD (for storage costs) and APT (for transaction fees). In this demo, we get both from the faucet.

Key code:

src/components/StorageAccountManager.tsx:33-36

const { storageAccountAddress } = useStorageAccount({
  client: shelbyClient,
  wallet,
});

src/components/StorageAccountManager.tsx:54-70

const handleFundAccount = useCallback(async () => {
  if (!storageAddressStr) return;

  try {
    setStatusMessage("Funding account with ShelbyUSD and APT...");
    await fundAccount(storageAddressStr);
    setIsFunded(true);
    // ...
  } catch (error) {
    // ...
  }
}, [storageAddressStr, fundAccount, onAccountFunded]);

The useFundAccount hook handles parallel funding with retry logic:

src/hooks/useFundAccount.ts:67-84

await Promise.all([
  withRetry(() =>
    shelbyClient.fundAccountWithShelbyUSD({
      address: storageAccountAddress,
      amount: DEFAULT_FUNDING_AMOUNT,
    }),
  ).then(() => {
    results.shelbyUsd = true;
  }),
  withRetry(() =>
    shelbyClient.fundAccountWithAPT({
      address: storageAccountAddress,
      amount: DEFAULT_FUNDING_AMOUNT,
    }),
  ).then(() => {
    results.apt = true;
  }),
]);

Step 3: Upload Blobs

Select a file, sign the transaction with your wallet, and the file is stored on Shelby. Blobs are retrievable via a simple HTTP GET request.

Key code:

src/components/BlobUploader.tsx:29-34

const { storageAccountAddress, signAndSubmitTransaction } = useStorageAccount({
  client: shelbyClient,
  wallet,
});

src/components/BlobUploader.tsx:36-38

const { mutateAsync: uploadBlobs, isPending: isUploading } = useUploadBlobs({
  client: shelbyClient,
});

src/components/BlobUploader.tsx:85-97

await uploadBlobs({
  signer: {
    account: storageAccountAddress,
    signAndSubmitTransaction,
  },
  blobs: [
    {
      blobName: selectedFile.name,
      blobData,
    },
  ],
  expirationMicros, // 30 days from now
});

src/components/BlobUploader.tsx:100

const blobUrl = `https://api.shelbynet.shelby.xyz/shelby/v1/blobs/${storageAccountAddress.toString()}/${selectedFile.name}`;

Project Structure

src/
├── app/
│   └── page.tsx             # Main page
├── components/
│   ├── WalletProvider.tsx   # Solana client setup
│   ├── Header.tsx           # Wallet connection UI
│   ├── StorageAccountManager.tsx  # Funding flow
│   └── BlobUploader.tsx     # File upload flow
├── hooks/
│   └── useFundAccount.ts    # Funding with retry logic
└── utils/
    └── shelbyClient.ts      # Shared Shelby client

Troubleshooting

Common Issues

"No wallets discovered"

  • Install a Solana wallet extension (Solflare, Phantom, etc.)
  • Enable the extension for the site and refresh

"INSUFFICIENT_BALANCE_FOR_TRANSACTION_FEE" error

  • Your storage account needs APT tokens for gas fees
  • Click "Fund Account" to receive tokens

"E_INSUFFICIENT_FUNDS" error

  • Your storage account needs ShelbyUSD for storage costs
  • Click "Fund Account" to receive tokens

Funding fails repeatedly

  • Faucet has rate limits; wait 30 seconds between attempts
  • Obtain API key from geomi.dev for increased limit

Upload stuck on "Uploading..."

  • Check your wallet for a pending signature request
  • Approve the transaction in your wallet popup

Next Steps

About

Get started with Shelby on Solana

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages