Skip to main content

useReadContract

Hook to read from smart contracts with automatic decryption of encrypted return values.

Usage​

import { useReadContract } from '@fhevm/sdk';

function BalanceDisplay() {
const { decryptedData, isLoading, isDecrypting } = useReadContract({
address: '0x123...',
abi: tokenAbi,
functionName: 'balanceOf',
args: [userAddress],
decrypt: true,
});

if (isLoading || isDecrypting) return <div>Loading...</div>;

return <div>Balance: {decryptedData?.toString()}</div>;
}

Parameters​

type UseReadContractParameters<TAbi extends Abi = Abi> = {
address?: `0x${string}`;
abi?: TAbi;
name?: string;
functionName: string;
args?: any[];
enabled?: boolean;
watch?: boolean;
decrypt?: boolean;
};
ParameterTypeDefaultDescription
address0x${string}-Contract address
abiAbi-Contract ABI
namestring-Contract name from config
functionNamestringRequiredFunction to call
argsany[][]Function arguments
enabledbooleantrueEnable/disable fetching
watchbooleanfalsePoll for updates every 5s
decryptbooleantrueAuto-decrypt encrypted values

Return Value​

type UseReadContractReturnType = {
data: any;
encryptedData: string | undefined;
decryptedData: string | bigint | boolean | undefined;
isLoading: boolean;
isDecrypting: boolean;
isError: boolean;
error: Error | null;
refetch: () => Promise<void>;
};
PropertyTypeDescription
dataanyRaw data from contract
encryptedDatastring | undefinedEncrypted handle if applicable
decryptedDatastring | bigint | boolean | undefinedDecrypted value
isLoadingbooleanInitial loading state
isDecryptingbooleanDecryption in progress
isErrorbooleanError occurred
errorError | nullError object
refetch() => Promise<void>Manually refetch data

Examples​

Basic Reading​

function TokenName() {
const { data, isLoading } = useReadContract({
address: '0x123...',
abi: tokenAbi,
functionName: 'name',
});

if (isLoading) return <div>Loading...</div>;

return <div>Token: {data}</div>;
}

Reading with Decryption​

function EncryptedBalance() {
const { decryptedData, isLoading, isDecrypting } = useReadContract({
address: '0x123...',
abi: encryptedTokenAbi,
functionName: 'balanceOf',
args: [userAddress],
decrypt: true, // Enable auto-decryption
});

if (isLoading) return <div>Loading...</div>;
if (isDecrypting) return <div>Decrypting...</div>;

return <div>Balance: {decryptedData?.toString()}</div>;
}

Using Named Contracts​

function NamedContractRead() {
const { data } = useReadContract({
name: 'myToken', // From config
functionName: 'totalSupply',
});

return <div>Total Supply: {data?.toString()}</div>;
}

With Arguments​

function AllowanceChecker({ owner, spender }: Props) {
const { data } = useReadContract({
address: '0x123...',
abi: tokenAbi,
functionName: 'allowance',
args: [owner, spender],
});

return <div>Allowance: {data?.toString()}</div>;
}

Conditional Fetching​

function ConditionalRead({ shouldFetch }: { shouldFetch: boolean }) {
const { data, isLoading } = useReadContract({
address: '0x123...',
abi: tokenAbi,
functionName: 'getData',
enabled: shouldFetch, // Only fetch when enabled
});

if (!shouldFetch) return <div>Disabled</div>;
if (isLoading) return <div>Loading...</div>;

return <div>Data: {data}</div>;
}

Watching for Changes​

function LiveBalance() {
const { decryptedData, isDecrypting } = useReadContract({
address: '0x123...',
abi: tokenAbi,
functionName: 'balanceOf',
args: [userAddress],
watch: true, // Poll every 5 seconds
decrypt: true,
});

return (
<div>
Balance: {decryptedData?.toString()}
{isDecrypting && ' (updating...)'}
</div>
);
}

Manual Refetch​

function ManualRefetch() {
const { data, refetch, isLoading } = useReadContract({
address: '0x123...',
abi: tokenAbi,
functionName: 'getData',
});

return (
<div>
<div>Data: {data}</div>
<button onClick={() => refetch()} disabled={isLoading}>
Refresh
</button>
</div>
);
}

Error Handling​

function WithErrorHandling() {
const { data, error, isError, isLoading } = useReadContract({
address: '0x123...',
abi: tokenAbi,
functionName: 'getData',
});

if (isLoading) return <div>Loading...</div>;

if (isError) {
return (
<div className="error">
Error: {error?.message}
</div>
);
}

return <div>Data: {data}</div>;
}

Both Encrypted and Raw Data​

function BothValues() {
const { encryptedData, decryptedData, isDecrypting } = useReadContract({
address: '0x123...',
abi: tokenAbi,
functionName: 'getBalance',
decrypt: true,
});

return (
<div>
<div>Encrypted Handle: {encryptedData}</div>
<div>
Decrypted Value:{' '}
{isDecrypting ? 'Decrypting...' : decryptedData?.toString()}
</div>
</div>
);
}

TypeScript Support​

import type { MyTokenAbi } from './types';

function TypedRead() {
const { data } = useReadContract<MyTokenAbi>({
address: '0x123...',
abi: myTokenAbi,
functionName: 'balanceOf', // Autocomplete works!
args: [userAddress], // Type-checked!
});

return <div>{data?.toString()}</div>;
}

Decryption Behavior​

Automatic Detection​

The hook automatically detects encrypted return values:

  • Checks if return value is a bytes32 handle (0x followed by 64 hex chars)
  • Excludes zero hash (0x000...000)
  • Only attempts decryption if decrypt: true

Decryption Process​

  1. Detects encrypted handle
  2. Requests user signature (once per session)
  3. Decrypts using FHEVM instance
  4. Caches result
  5. Returns decrypted value

Cache Integration​

Decrypted values are automatically cached based on:

  • Chain ID
  • User account
  • Contract address
  • Encrypted handle

Cache TTL is configured in FhevmConfig.

Best Practices​

1. Enable Decryption for Encrypted Data​

const { decryptedData } = useReadContract({
functionName: 'getBalance',
decrypt: true, // Important for encrypted values
});

2. Use Conditional Fetching​

const { data } = useReadContract({
functionName: 'getData',
enabled: Boolean(address), // Only fetch when address exists
});

3. Handle Loading States​

if (isLoading) return <Spinner />;
if (isDecrypting) return <span>Decrypting...</span>;
if (isError) return <Error message={error.message} />;
return <Display data={decryptedData} />;

4. Use Watch Sparingly​

// Only use watch for data that changes frequently
const { data } = useReadContract({
functionName: 'getLivePrice',
watch: true, // Polls every 5 seconds
});

Performance Tips​

Caching​

Enable caching in configuration:

const config = createConfig({
cache: {
enabled: true,
ttl: 60000, // 1 minute
},
});

Disable Unused Features​

const { data } = useReadContract({
functionName: 'getData',
decrypt: false, // Disable if data isn't encrypted
watch: false, // Disable polling if not needed
});

Notes​

Provider Required

This hook requires FhevmProvider to be present in the component tree.

Automatic Decryption

The hook automatically detects encrypted handles and decrypts them when decrypt: true. No manual intervention needed!

Signature Request

The first decryption operation will prompt the user to sign a decryption permission. This signature is cached for future operations.

Polling Interval

When watch: true, the hook polls every 5 seconds. This interval is not configurable in the current version.