Ethereum Kit

useStorageAccount

React hook for Shelby storage account operations with Ethereum wallets

The useStorageAccount hook provides a convenient way to interact with Shelby storage from React applications using a connected Ethereum wallet.

Overview

This hook:

  • Derives a Shelby storage account address from the connected Ethereum wallet
  • Provides functions to sign and submit transactions to Shelby
  • Handles the SIWE (Sign-In With Ethereum) authentication flow automatically

Import

import { useStorageAccount } from "@shelby-protocol/ethereum-kit/react";

Parameters

interface UseStorageAccountParams {
  /** The Shelby client instance */
  client: ShelbyClient;
  /** The connected Ethereum wallet from wagmi's useWalletClient() */
  wallet: EthereumWallet | null | undefined;
}
ParameterTypeRequiredDescription
clientShelbyClientYesThe Shelby client instance
walletEthereumWallet | nullYesThe connected Ethereum wallet

EthereumWallet Type

The EthereumWallet type is viem's WalletClient, which is what wagmi's useWalletClient() returns:

import type { WalletClient, Transport, Chain, Account } from "viem";

type EthereumWallet = WalletClient<Transport, Chain, Account>;

This provides full type safety and autocomplete when used with wagmi.

Return Value

interface UseStorageAccountResult {
  /** The derived Shelby storage account address */
  storageAccountAddress: AccountAddress | null;
  /** Sign a transaction for Shelby */
  signTransaction: (params: SignTransactionInput) => Promise<{
    authenticator: AccountAuthenticatorAbstraction;
    rawTransaction: AnyRawTransaction;
  }>;
  /** Submit a signed transaction to Shelby */
  submitTransaction: (
    params: SubmitTransactionInput
  ) => Promise<{ hash: string }>;
  /** Sign and submit a transaction in one call */
  signAndSubmitTransaction: (
    params: SignAndSubmitTransactionInput
  ) => Promise<{ hash: string }>;
}
PropertyTypeDescription
storageAccountAddressAccountAddress | nullThe derived storage account address
signTransactionfunctionSigns a transaction (returns authenticator)
submitTransactionfunctionSubmits a pre-signed transaction
signAndSubmitTransactionfunctionSigns and submits in one operation

Basic Usage

"use client";

import { useStorageAccount, Network } from "@shelby-protocol/ethereum-kit/react";
import { ShelbyClient } from "@shelby-protocol/sdk/browser";
import { useWalletClient } from "wagmi";
import { useMemo } from "react";

function StorageComponent() {
  const { data: wallet } = useWalletClient();

  // Create client (memoize to prevent recreation)
  const shelbyClient = useMemo(
    () =>
      new ShelbyClient({
        network: Network.SHELBYNET,
        apiKey: process.env.NEXT_PUBLIC_SHELBY_API_KEY!,
      }),
    []
  );

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

  if (!wallet) {
    return <p>Please connect your wallet</p>;
  }

  return (
    <div>
      <p>Ethereum Address: {wallet.account.address}</p>
      <p>Shelby Storage: {storageAccountAddress?.toString()}</p>
    </div>
  );
}

Domain Derivation

The storage account address is derived from:

  1. The connected wallet's Ethereum address
  2. The current domain (window.location.host)

The same Ethereum wallet will have different storage account addresses on different domains. This provides application-level isolation.

// On app1.com
const { storageAccountAddress } = useStorageAccount({...});
// storageAccountAddress = "0x1111..."

// On app2.com (same wallet)
const { storageAccountAddress } = useStorageAccount({...});
// storageAccountAddress = "0x2222..."

With File Upload

Use with @shelby-protocol/react for file uploads:

"use client";

import { useStorageAccount, Network } from "@shelby-protocol/ethereum-kit/react";
import { useUploadBlobs } from "@shelby-protocol/react";
import { ShelbyClient } from "@shelby-protocol/sdk/browser";
import { useWalletClient } from "wagmi";
import { useMemo } from "react";

function FileUploader() {
  const { data: wallet } = useWalletClient();
  
  const shelbyClient = useMemo(
    () =>
      new ShelbyClient({
        network: Network.SHELBYNET,
        apiKey: process.env.NEXT_PUBLIC_SHELBY_API_KEY!,
      }),
    []
  );

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

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

  const handleUpload = async (file: File) => {
    if (!storageAccountAddress) return;

    const buffer = await file.arrayBuffer();
    await uploadBlobs({
      signer: { account: storageAccountAddress, signAndSubmitTransaction },
      blobs: [
        {
          blobName: file.name,
          blobData: new Uint8Array(buffer),
        },
      ],
      expirationMicros: Date.now() * 1000 + 86400000000 * 30, // 30 days
    });
  };

  return (
    <div>
      <input
        type="file"
        onChange={(e) => e.target.files?.[0] && handleUpload(e.target.files[0])}
        disabled={!wallet || isPending}
      />
      {isPending && <p>Uploading...</p>}
    </div>
  );
}

How It Works

  1. Automatic Address Derivation: The hook automatically derives the storage account address from:

    • The connected wallet's Ethereum address
    • The current page's domain (window.location.host)
  2. SIWE Signing: When signing transactions, the hook uses Sign-In with Ethereum (SIWE) to create signatures that the Aptos blockchain can verify.

  3. Domain Isolation: Each dApp domain gets a unique storage account for the same wallet, providing application-level isolation.

Type Safety with wagmi

The hook is designed for seamless integration with wagmi's useWalletClient():

import { useWalletClient } from "wagmi";

function MyComponent() {
  // wallet is WalletClient<Transport, Chain, Account> | undefined
  const { data: wallet } = useWalletClient();

  // No type casting needed - EthereumWallet matches WalletClient
  const { storageAccountAddress } = useStorageAccount({
    client: shelbyClient,
    wallet
  });
}

This works with all wagmi-based wallet libraries:

  • RainbowKit
  • Web3Modal
  • ConnectKit
  • Dynamic