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;
}| Parameter | Type | Required | Description |
|---|---|---|---|
client | ShelbyClient | Yes | The Shelby client instance |
wallet | EthereumWallet | null | Yes | The 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 }>;
}| Property | Type | Description |
|---|---|---|
storageAccountAddress | AccountAddress | null | The derived storage account address |
signTransaction | function | Signs a transaction (returns authenticator) |
submitTransaction | function | Submits a pre-signed transaction |
signAndSubmitTransaction | function | Signs 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:
- The connected wallet's Ethereum address
- 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
-
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)
-
SIWE Signing: When signing transactions, the hook uses Sign-In with Ethereum (SIWE) to create signatures that the Aptos blockchain can verify.
-
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