React SDK

DApp Example

Learn how to build a file upload page with wallet adapter integration

Overview

In this guide, we will walk you through building a complete file upload page that integrates with the Aptos Wallet Adapter and uses the Shelby React SDK to upload files to the Shelby network.

This guide assumes you have a React application set up and understand the basics of React hooks and file handling.

Getting Started

Installation

First, install the required dependencies:

npm install @shelby-protocol/react @shelby-protocol/sdk @aptos-labs/ts-sdk @aptos-labs/wallet-adapter-react @tanstack/react-query

Setting up Providers

Wrap your application with the necessary providers. Create a providers component:

AppProviders.tsx
"use client";

import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { AptosWalletAdapterProvider } from "@aptos-labs/wallet-adapter-react";
import { Network } from "@aptos-labs/ts-sdk";
import type { PropsWithChildren } from "react";

const queryClient = new QueryClient();

export function AppProviders({ children }: PropsWithChildren) {
  return (
    <QueryClientProvider client={queryClient}>
      <AptosWalletAdapterProvider
        autoConnect
        dappConfig={{ 
          network: Network.SHELBYNET,
          // It is recommended to add your API key to this configuration object.
          aptosApiKeys: { shelbynet: process.env.NEXT_PUBLIC_SHELBYNET_API_KEY },
        }}
      >
        {children}
      </AptosWalletAdapterProvider>
    </QueryClientProvider>
  );
}

Then wrap your app:

App.tsx
import type { PropsWithChildren } from "react";
import { AppProviders } from "./AppProviders";

function App({ children }: PropsWithChildren) {
  return (
    <AppProviders>
      {children}
    </AppProviders>
  );
}

Creating the Shelby Client

Create a shared Shelby client instance. You can create a utility file:

lib/shelby.ts
import { ShelbyClient } from "@shelby-protocol/sdk/browser";
import { Network } from "@aptos-labs/ts-sdk";

export const shelbyClient = new ShelbyClient({
  network: Network.SHELBYNET,
});

It is recommended to create a shared instance of the Shelby client and use it throughout your application. This can be done in a utility file or a context provider.

Building the Upload Component

Now let's create a complete upload component with file input:

components/FileUpload.tsx
"use client";

import { useState, useCallback } from "react";
import { useWallet } from "@aptos-labs/wallet-adapter-react";
import { useUploadBlobs } from "@shelby-protocol/react";
import { shelbyClient } from "@/lib/shelby";

export function FileUpload() {
  const { account, signAndSubmitTransaction, connected } = useWallet();
  const [selectedFiles, setSelectedFiles] = useState<File[]>([]);

  const uploadBlobs = useUploadBlobs({
    client: shelbyClient,
    onSuccess: () => {
      alert("Files uploaded successfully!");
      setSelectedFiles([]);
    },
    onError: (error) => {
      alert(`Upload failed: ${error.message}`);
    },
  });

  const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = Array.from(e.target.files || []);
    setSelectedFiles(files);
  };

  const handleUpload = useCallback(async () => {
    if (!connected || !account || !signAndSubmitTransaction) {
      alert("Please connect your wallet first");
      return;
    }

    if (selectedFiles.length === 0) {
      alert("Please select at least one file");
      return;
    }

    // Convert files to Uint8Array
    const blobs = await Promise.all(
      selectedFiles.map(async (file) => {
        const arrayBuffer = await file.arrayBuffer();
        return {
          blobName: file.name,
          blobData: new Uint8Array(arrayBuffer),
        };
      }),
    );

    // Set expiration time to 7 days from now (in microseconds)
    const expirationMicros =
      (Date.now() * 1000) + (7 * 24 * 60 * 60 * 1000 * 1000);

    // Upload the blobs to the Shelby network
    uploadBlobs.mutate({
      signer: { account: account.accountAddress, signAndSubmitTransaction },
      blobs,
      expirationMicros,
    });
  }, [connected, account, signAndSubmitTransaction, selectedFiles, uploadBlobs]);

  return (
    <div>
      <h1>Upload Files to Shelby</h1>

      {!connected && (
        <div>
          <p>Please connect your wallet to upload files.</p>
        </div>
      )}

      {connected && (
        <div>
          <div>
            <label>
              Select Files
            </label>
            <input
              type="file"
              multiple
              onChange={handleFileSelect}
            />
          </div>
          <br/>
          {selectedFiles.length > 0 && (
            <div>
              <p>Selected Files:</p>
              <ul>
                {selectedFiles.map((file, index) => (
                  <li key={index}>
                    {file.name} ({(file.size / 1024).toFixed(2)} KB)
                  </li>
                ))}
              </ul>
            </div>
          )}
          <br/>
          <button
            onClick={handleUpload}
            disabled={uploadBlobs.isPending || selectedFiles.length === 0}
          >
            {uploadBlobs.isPending ? "Uploading..." : "Upload Files"}
          </button>
          <br/>
          {uploadBlobs.isError && (
            <div>
              <p>
                Error: {uploadBlobs.error?.message}
              </p>
            </div>
          )}
        </div>
      )}
    </div>
  );
}

Adding Wallet Connection UI

Create a simple wallet connection component:

components/WalletConnection.tsx
"use client";

import { useWallet } from "@aptos-labs/wallet-adapter-react";

export function WalletConnection() {
  const { connect, disconnect, connected, account, wallet } = useWallet();

  if (connected && account) {
    return (
      <div>
        <div>
          <p>Connected</p>
          <p>
            {account.address.slice(0, 6)}...{account.address.slice(-4)}
          </p>
        </div>
        <button onClick={disconnect}>
          Disconnect
        </button>
      </div>
    );
  }

  return (
    <div>
      <p>Connect your wallet</p>
      <button onClick={connect}>
        Connect Wallet
      </button>
    </div>
  );
}

For a more polished wallet connection experience, consider using one of the official wallet adapter UI packages. The Aptos Wallet Adapter provides UI components for popular design systems including shadcn/ui, Ant Design, and MUI. These packages provide pre-built wallet selector modals and connect buttons that you can integrate into your application.

Complete Example Page

Here's a complete example page that brings everything together:

pages/UploadPage.tsx
"use client";

import { FileUpload } from "@/components/FileUpload";
import { WalletConnection } from "@/components/WalletConnection";

export default function UploadPage() {
  return (
    <div>
      <header>
        <div>
          <h1>Shelby File Upload</h1>
          <WalletConnection />
        </div>
      </header>
      <main>
        <FileUpload />
      </main>
    </div>
  );
}

Conclusion

You now have a complete file upload page that integrates with the Aptos Wallet Adapter! The page allows users to:

  • Connect their wallet
  • Select multiple files
  • Upload files to the Shelby network
  • See upload status and errors

This example demonstrates the core functionality of the Shelby React SDK. For more advanced usage and additional features, check out the mutation hooks documentation and query hooks documentation.