Account Abstraction
Account abstraction (AA) replaces traditional externally owned accounts (EOAs) with programmable smart contract accounts. This unlocks capabilities that EOAs alone cannot provide:- Gas sponsorship — let users transact without holding native tokens for gas fees
- Batched transactions — bundle multiple operations into a single atomic transaction
- Session keys — grant temporary, scoped permissions without exposing the primary key
- Custom authorization — implement multi-factor auth, spending limits, or recovery logic at the account level
- Programmable accounts — create accounts with built-in rules for corporate or multi-user scenarios
| EIP-4337 | EIP-7702 | |
|---|---|---|
| How it works | UserOperations processed by bundlers via an EntryPoint contract | EOA delegates to a smart contract via a type-4 transaction |
| Account address | New smart contract address (different from EOA) | Same as your EOA |
| Chain support | All EVM chains | Requires chain-level support |
| Infrastructure | Bundler + Paymaster | Provider-dependent |
| Gas overhead | Higher (extra validation steps) | Lower (native execution) |
| Maturity | Production-ready | Emerging |
| Best for | Maximum compatibility, dedicated smart wallet | Keeping EOA address, simpler flows |
Para Integrations
Para acts as the signer (EOA) that controls smart contract wallets — it is not a smart wallet itself. Para handles secure key management via MPC, while the AA provider deploys and manages the smart contract account on-chain. This means your users’ private keys remain secure and under their control regardless of which provider you choose. Para wraps each provider in a sharedSmartAccount interface. Whether you use EIP-4337 or EIP-7702 mode, your application code stays the same — you can swap providers or switch between standards without rewriting transaction logic.
Every provider integration is available as an action function (createXxxSmartAccount) — an imperative async function you can call anywhere, including outside React components, inside custom hooks, or in server-side code. React Native applications also get a hook (useXxxSmartAccount) — a declarative wrapper powered by React Query that handles initialization, caching, and automatic re-creation when config changes.
Both return the same SmartAccount object:
import type { Chain, Hex, LocalAccount, TransactionReceipt } from "viem";
interface SmartAccount {
smartAccountAddress: Hex; // On-chain account address
signer: LocalAccount; // Para viem signer
chain: Chain; // Configured chain
mode: "4337" | "7702"; // Account type
provider: string; // Provider name
// Send a single transaction and wait for receipt
sendTransaction(params: {
to: Hex;
value?: bigint;
data?: Hex;
}): Promise<TransactionReceipt>;
// Send batched transactions atomically
sendBatchTransaction(calls: Array<{
to: Hex;
value?: bigint;
data?: Hex;
}>): Promise<TransactionReceipt>;
client: any; // Provider-specific client for advanced usage
}
mode: "7702", the return type narrows to include an optional delegationAddress field — the smart
contract the EOA delegates to. When using mode: "4337", smartAccountAddress is the counterfactual smart
contract address (different from signer.address).
Provider Comparison
| Provider | 4337 | 7702 | Chain Restrictions | Key Feature |
|---|---|---|---|---|
| Alchemy | ✓ | ✓ | Alchemy-supported chains | Modular accounts, gas policies |
| ZeroDev | ✓ | ✓ | Any EVM chain | Kernel accounts, session keys, plugins |
| Pimlico | ✓ | ✓ | Any EVM chain | Permissionless SDK, flexible paymaster |
| Biconomy | ✓ | ✓ | MEE-supported chains | Cross-chain orchestration via MEE |
| Thirdweb | ✓ | ✓ | Any EVM chain | Smart wallets, gas sponsorship |
| Gelato | ✓ | Any EVM chain | Native 7702, built-in gas sponsorship | |
| Porto | ✓ | Multiple chains | 7702-native relay, merchant-based gas sponsorship | |
| Safe | ✓ | Any EVM chain | Multi-sig, modular accounts | |
| Rhinestone | ✓ | Any EVM chain | Cross-chain orchestration | |
| Coinbase Developer Platform | ✓ | Base, Base Sepolia only | Coinbase smart accounts |
Provider Guides
Select a provider below for installation and usage instructions.- Alchemy
- ZeroDev
- Pimlico
- Biconomy
- Thirdweb
- Gelato
- Porto
- Safe
- Rhinestone
- Coinbase Developer Platform
Alchemy Account Kit provides modular smart accounts with built-in gas sponsorship via Alchemy’s Gas Manager. Create and manage smart accounts, submit gasless transactions, and execute batched UserOperations — all without requiring users to leave your application. Supports both EIP-4337 and EIP-7702 modes.
Setup
- Create an Alchemy account and obtain your API key and Gas Policy ID from the Alchemy dashboard.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-alchemy viem
Usage
- useAlchemySmartAccount
- createAlchemySmartAccount
The simplest way to integrate Alchemy smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
AlchemySmartAccount.tsx
import { useAlchemySmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
const ALCHEMY_API_KEY = process.env.EXPO_PUBLIC_ALCHEMY_API_KEY!;
const GAS_POLICY_ID = process.env.EXPO_PUBLIC_ALCHEMY_GAS_POLICY_ID!;
export function AlchemySmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// account client setup, and paymaster configuration internally.
// It re-creates the account automatically if any config value changes.
const { smartAccount, isLoading, error } = useAlchemySmartAccount({
apiKey: ALCHEMY_API_KEY,
chain: sepolia,
gasPolicyId: GAS_POLICY_ID, // enables gas sponsorship
mode: "4337", // or "7702" — see EIP comparison above
});
// Wrap sendTransaction in a mutation for loading/error state management.
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
// Wrap sendBatchTransaction for executing multiple calls atomically.
// All calls are bundled into a single on-chain transaction.
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
alchemy-action.ts
import { createAlchemySmartAccount } from "@getpara/aa-alchemy";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createAlchemySmartAccount({
para,
apiKey: "YOUR_ALCHEMY_API_KEY",
chain: sepolia,
gasPolicyId: "YOUR_GAS_POLICY_ID",
mode: "4337", // or "7702"
});
if (smartAccount) {
// Send a single transaction
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
// Send batched transactions
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
Alchemy requires chains from
@account-kit/infra (e.g. sepolia, baseSepolia). Plain viem chains are automatically mapped if an Alchemy equivalent exists. For gasless transactions, set up a Gas Manager Policy in your Alchemy Dashboard and pass the policy ID as gasPolicyId.ZeroDev is an embedded AA wallet powering many smart accounts across EVM chains. Known for its extensive feature set including gas sponsorship, session keys, recovery, multisig, and chain abstraction. Supports both EIP-4337 and EIP-7702 modes.
Setup
- Create a ZeroDev account and obtain your Project ID from the ZeroDev dashboard.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-zerodev viem
Usage
- useZeroDevSmartAccount
- createZeroDevSmartAccount
The simplest way to integrate ZeroDev smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
ZeroDevSmartAccount.tsx
import { useZeroDevSmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
const ZERODEV_PROJECT_ID = process.env.EXPO_PUBLIC_ZERODEV_PROJECT_ID!;
export function ZeroDevSmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// Kernel account setup, and ECDSA validator configuration internally.
const { smartAccount, isLoading, error } = useZeroDevSmartAccount({
projectId: ZERODEV_PROJECT_ID,
chain: sepolia,
mode: "4337", // or "7702"
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
zerodev-action.ts
import { createZeroDevSmartAccount } from "@getpara/aa-zerodev";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createZeroDevSmartAccount({
para,
projectId: "YOUR_ZERODEV_PROJECT_ID",
chain: sepolia,
mode: "4337", // or "7702"
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
You can optionally pass
bundlerUrl and paymasterUrl to use custom infrastructure instead of ZeroDev’s defaults. For more on managing your ZeroDev project and RPC endpoints, see the ZeroDev RPC documentation.Pimlico provides flexible bundler and paymaster infrastructure for account abstraction. Built on the permissionless.js SDK, it supports a wide range of smart account types and any EVM chain. Supports both EIP-4337 and EIP-7702 modes.
Setup
- Create a Pimlico account and obtain your API key from the Pimlico dashboard.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-pimlico viem
Usage
- usePimlicoSmartAccount
- createPimlicoSmartAccount
The simplest way to integrate Pimlico smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
PimlicoSmartAccount.tsx
import { usePimlicoSmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
const PIMLICO_API_KEY = process.env.EXPO_PUBLIC_PIMLICO_API_KEY!;
export function PimlicoSmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// simple account setup, and Pimlico paymaster configuration internally.
const { smartAccount, isLoading, error } = usePimlicoSmartAccount({
apiKey: PIMLICO_API_KEY,
chain: sepolia,
mode: "4337", // or "7702"
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
pimlico-action.ts
import { createPimlicoSmartAccount } from "@getpara/aa-pimlico";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createPimlicoSmartAccount({
para,
apiKey: "YOUR_PIMLICO_API_KEY",
chain: sepolia,
mode: "4337", // or "7702"
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
The Pimlico bundler/paymaster URL is automatically constructed from your API key and chain name. You can override it with a custom
rpcUrl. Pimlico’s permissionless.js also supports other account types (Safe, Kernel, Biconomy, SimpleAccount) — see the permissionless.js tutorial.Biconomy provides the Nexus smart account with cross-chain transaction orchestration via the MEE (Multi-chain Execution Environment). Supports both EIP-4337 and EIP-7702 modes.
Setup
- Create a Biconomy account and obtain your API key from the Biconomy dashboard.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-biconomy viem
Usage
- useBiconomySmartAccount
- createBiconomySmartAccount
The simplest way to integrate Biconomy smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
BiconomySmartAccount.tsx
import { useBiconomySmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
const BICONOMY_API_KEY = process.env.EXPO_PUBLIC_BICONOMY_API_KEY!;
export function BiconomySmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// Nexus account setup, and MEE client configuration internally.
const { smartAccount, isLoading, error } = useBiconomySmartAccount({
apiKey: BICONOMY_API_KEY,
chain: sepolia,
mode: "4337", // or "7702"
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
biconomy-action.ts
import { createBiconomySmartAccount } from "@getpara/aa-biconomy";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createBiconomySmartAccount({
para,
apiKey: "YOUR_BICONOMY_API_KEY",
chain: sepolia,
mode: "4337", // or "7702"
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
Biconomy transactions are executed via the MEE (Multi-chain Execution Environment). You can optionally pass a custom
meeUrl to use your own MEE node.Thirdweb provides smart wallets with gas sponsorship and a broad ecosystem of tools. Supports both EIP-4337 and EIP-7702 modes.
Setup
- Create a Thirdweb account and obtain your Client ID from the Thirdweb dashboard.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-thirdweb viem
Usage
- useThirdwebSmartAccount
- createThirdwebSmartAccount
The simplest way to integrate Thirdweb smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
ThirdwebSmartAccount.tsx
import { useThirdwebSmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
const THIRDWEB_CLIENT_ID = process.env.EXPO_PUBLIC_THIRDWEB_CLIENT_ID!;
export function ThirdwebSmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// smart wallet setup, and gas sponsorship configuration internally.
const { smartAccount, isLoading, error } = useThirdwebSmartAccount({
clientId: THIRDWEB_CLIENT_ID,
chain: sepolia,
sponsorGas: true, // enable gas sponsorship (default)
mode: "4337", // or "7702"
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
thirdweb-action.ts
import { createThirdwebSmartAccount } from "@getpara/aa-thirdweb";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createThirdwebSmartAccount({
para,
clientId: "YOUR_THIRDWEB_CLIENT_ID",
chain: sepolia,
sponsorGas: true,
mode: "4337", // or "7702"
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
Set
sponsorGas: false to disable gas sponsorship. In EIP-4337 mode, you can optionally provide factoryAddress and accountAddress for custom smart wallet deployments.Gelato provides native EIP-7702 smart accounts with built-in gas sponsorship via Gelato’s relay infrastructure. No separate paymaster configuration is needed.
Setup
- Create a Gelato account and obtain your API key from the Gelato dashboard.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-gelato viem
Usage
- useGelatoSmartAccount
- createGelatoSmartAccount
The simplest way to integrate Gelato smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
GelatoSmartAccount.tsx
import { useGelatoSmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
const GELATO_API_KEY = process.env.EXPO_PUBLIC_GELATO_API_KEY!;
export function GelatoSmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// Gelato account setup, and 7702 delegation internally.
// Gelato only supports EIP-7702 mode.
const { smartAccount, isLoading, error } = useGelatoSmartAccount({
apiKey: GELATO_API_KEY,
chain: sepolia,
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
gelato-action.ts
import { createGelatoSmartAccount } from "@getpara/aa-gelato";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createGelatoSmartAccount({
para,
apiKey: "YOUR_GELATO_API_KEY",
chain: sepolia,
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
Gelato only supports EIP-7702 mode. Gas sponsorship is built into Gelato’s relay infrastructure — no separate paymaster configuration is needed.
Porto is a 7702-native relay with merchant-based gas sponsorship, supporting multiple chains including Base, Optimism, Arbitrum, and Ethereum mainnet.
Setup
Porto requires no external API key. On testnets, gas is sponsored automatically. For mainnet, configure a merchant account in your Porto setup.Install the required dependencies:npm install @getpara/react-native-wallet @getpara/aa-porto viem
Usage
- usePortoSmartAccount
- createPortoSmartAccount
The simplest way to integrate Porto smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
PortoSmartAccount.tsx
import { usePortoSmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { parseEther } from "viem";
import { base } from "viem/chains";
export function PortoSmartAccount() {
// Initialize the smart account. The hook handles Para signer creation
// and Porto relay configuration internally.
// Testnets: gas is sponsored by default, no merchantUrl needed.
// Mainnet: merchantUrl is required for gas sponsorship.
const { smartAccount, isLoading, error } = usePortoSmartAccount({
chain: base,
merchantUrl: "/porto/merchant", // required for mainnet
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
porto-action.ts
import { createPortoSmartAccount } from "@getpara/aa-porto";
import { parseEther } from "viem";
import { base } from "viem/chains";
// `para` is your authenticated Para instance
const smartAccount = await createPortoSmartAccount({
para,
chain: base,
merchantUrl: "/porto/merchant", // required for mainnet
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
Porto only supports EIP-7702 mode. The Porto relay supports multiple chains including Base, Optimism, Arbitrum, Ethereum, and several testnets. On testnets, gas fees are sponsored by default. For mainnet, a merchant account is required — pass
merchantUrl to enable gas sponsorship.Safe provides battle-tested multi-signature smart accounts with modular extension support, deployed on hundreds of EVM chains. Uses Pimlico for bundler and paymaster infrastructure. Supports EIP-4337 mode only.
Setup
- Obtain a Pimlico API key for bundler and paymaster infrastructure.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-safe viem
Usage
- useSafeSmartAccount
- createSafeSmartAccount
The simplest way to integrate Safe smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
SafeSmartAccount.tsx
import { useSafeSmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
const PIMLICO_API_KEY = process.env.EXPO_PUBLIC_PIMLICO_API_KEY!;
export function SafeSmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// Safe account setup, and Pimlico paymaster configuration internally.
// Safe only supports EIP-4337 mode.
const { smartAccount, isLoading, error } = useSafeSmartAccount({
pimlicoApiKey: PIMLICO_API_KEY,
chain: sepolia,
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
safe-action.ts
import { createSafeSmartAccount } from "@getpara/aa-safe";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createSafeSmartAccount({
para,
pimlicoApiKey: "YOUR_PIMLICO_API_KEY",
chain: sepolia,
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
Safe only supports EIP-4337 mode. You can optionally pass
safeVersion (default: "1.4.1") and saltNonce for deterministic address generation.Rhinestone enables cross-chain smart account orchestration with automatic bridging. Built on the modular account stack, it supports cross-chain token transfers and transactions from a single account. Supports EIP-4337 mode only.
Setup
- Obtain a Rhinestone API key and a Pimlico API key.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-rhinestone viem
Usage
- useRhinestoneSmartAccount
- createRhinestoneSmartAccount
The simplest way to integrate Rhinestone smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
RhinestoneSmartAccount.tsx
import { useRhinestoneSmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
const RHINESTONE_API_KEY = process.env.EXPO_PUBLIC_RHINESTONE_API_KEY!;
const PIMLICO_API_KEY = process.env.EXPO_PUBLIC_PIMLICO_API_KEY!;
export function RhinestoneSmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// Rhinestone account setup, and orchestrator configuration internally.
// Rhinestone only supports EIP-4337 mode.
const { smartAccount, isLoading, error } = useRhinestoneSmartAccount({
chain: sepolia,
rhinestoneApiKey: RHINESTONE_API_KEY,
pimlicoApiKey: PIMLICO_API_KEY,
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
rhinestone-action.ts
import { createRhinestoneSmartAccount } from "@getpara/aa-rhinestone";
import { sepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createRhinestoneSmartAccount({
para,
chain: sepolia,
rhinestoneApiKey: "YOUR_RHINESTONE_API_KEY",
pimlicoApiKey: "YOUR_PIMLICO_API_KEY",
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
Rhinestone only supports EIP-4337 mode. Both
rhinestoneApiKey and pimlicoApiKey are optional but recommended for production use. For advanced cross-chain use cases with automatic bridging, see the Rhinestone documentation.Coinbase Developer Platform (CDP) provides Coinbase smart accounts on Base and Base Sepolia. Uses viem’s built-in
toCoinbaseSmartAccount with CDP’s bundler and paymaster. Supports EIP-4337 mode only.Setup
- Create a Coinbase Developer Platform account and obtain your RPC token.
- Install the required dependencies:
npm install @getpara/react-native-wallet @getpara/aa-cdp viem
Usage
- useCDPSmartAccount
- createCDPSmartAccount
The simplest way to integrate CDP smart accounts into your React Native application. The hook manages initialization, caching, and re-creation automatically via React Query.
CDPSmartAccount.tsx
import { useCDPSmartAccount } from "@getpara/react-native-wallet";
import { useMutation } from "@tanstack/react-query";
import { View, Text, Button } from "react-native";
import { baseSepolia } from "viem/chains";
import { parseEther } from "viem";
const CDP_RPC_TOKEN = process.env.EXPO_PUBLIC_CDP_RPC_TOKEN!;
export function CDPSmartAccount() {
// Initialize the smart account. The hook handles Para signer creation,
// Coinbase account setup, and CDP paymaster configuration internally.
// CDP only supports EIP-4337 mode on Base and Base Sepolia.
const { smartAccount, isLoading, error } = useCDPSmartAccount({
rpcToken: CDP_RPC_TOKEN,
chain: baseSepolia,
});
const {
mutate: sendTransaction,
isPending: isSending,
error: sendError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
},
onSuccess: (receipt) => {
console.log("Transaction hash:", receipt.transactionHash);
},
});
const {
mutate: sendBatch,
isPending: isBatching,
error: batchError,
} = useMutation({
mutationFn: async () => {
if (!smartAccount) throw new Error("Smart account not ready");
return smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
},
onSuccess: (receipt) => {
console.log("Batch tx hash:", receipt.transactionHash);
},
});
if (isLoading) return <Text>Setting up smart account...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (!smartAccount) return null;
return (
<View>
<Text>Smart Account: {smartAccount.smartAccountAddress}</Text>
<Text>Mode: {smartAccount.mode}</Text>
<Button
title={isSending ? "Sending..." : "Send Transaction"}
onPress={() => sendTransaction()}
disabled={isSending}
/>
{sendError && <Text>Send failed: {sendError.message}</Text>}
<Button
title={isBatching ? "Batching..." : "Send Batch"}
onPress={() => sendBatch()}
disabled={isBatching}
/>
{batchError && <Text>Batch failed: {batchError.message}</Text>}
</View>
);
}
For use outside React components or when you need more control over initialization.
cdp-action.ts
import { createCDPSmartAccount } from "@getpara/aa-cdp";
import { baseSepolia } from "viem/chains";
import { parseEther } from "viem";
// `para` is your authenticated Para instance
const smartAccount = await createCDPSmartAccount({
para,
rpcToken: "YOUR_CDP_RPC_TOKEN",
chain: baseSepolia,
});
if (smartAccount) {
const receipt = await smartAccount.sendTransaction({
to: "0xRecipient",
value: parseEther("0.01"),
});
const batchReceipt = await smartAccount.sendBatchTransaction([
{ to: "0xRecipientA", value: parseEther("0.01") },
{ to: "0xRecipientB", data: "0xencodedCallData" },
]);
}
CDP only supports EIP-4337 mode and is limited to Base and Base Sepolia chains. No additional bundler packages are needed — CDP uses viem’s built-in
toCoinbaseSmartAccount.