Prerequisites
Para Setup Required
Provider Setup
- Alchemy
- ZeroDev
- Gelato
- Pimlico
- Biconomy
- Safe
- Thirdweb
- Rhinestone
Installation
Copy
Ask AI
npm install @account-kit/smart-contracts @account-kit/infra @aa-sdk/core viem
Configuration
useSmartAccountClient.ts
Copy
Ask AI
import { useState, useEffect, useRef } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { createModularAccountV2Client, type ModularAccountV2Client } from "@account-kit/smart-contracts";
import { alchemy, sepolia } from "@account-kit/infra";
import { WalletClientSigner } from "@aa-sdk/core";
import { createWalletClient, http, type Address } from "viem";
const ALCHEMY_API_KEY = process.env.NEXT_PUBLIC_ALCHEMY_API_KEY!;
const GAS_POLICY_ID = process.env.NEXT_PUBLIC_ALCHEMY_GAS_POLICY_ID;
const CHAIN = sepolia;
export function useSmartAccountClient() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const [client, setClient] = useState<ModularAccountV2Client | null>(null);
const [address, setAddress] = useState<Address | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const initializedForAddress = useRef<string | null>(null);
useEffect(() => {
if (!viemAccount || isViemLoading || !ALCHEMY_API_KEY) return;
if (initializedForAddress.current === viemAccount.address) return;
const initializeClient = async () => {
setIsLoading(true);
setError(null);
try {
const walletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(),
});
const signer = new WalletClientSigner(walletClient, "para");
const accountClient = await createModularAccountV2Client({
transport: alchemy({ apiKey: ALCHEMY_API_KEY }),
chain: sepolia,
signer,
policyId: GAS_POLICY_ID || undefined,
});
setClient(accountClient);
setAddress(accountClient.account.address);
initializedForAddress.current = viemAccount.address;
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize"));
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, isViemLoading]);
return { client, address, isLoading: isLoading || isViemLoading, error };
}
Installation
Copy
Ask AI
npm install @zerodev/sdk @zerodev/ecdsa-validator viem
Configuration
useSmartAccountClient.ts
Copy
Ask AI
import { useState, useEffect, useRef } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { createKernelAccount, createKernelAccountClient, createZeroDevPaymasterClient } from "@zerodev/sdk";
import { getEntryPoint, KERNEL_V3_1 } from "@zerodev/sdk/constants";
import { signerToEcdsaValidator } from "@zerodev/ecdsa-validator";
import { createWalletClient, createPublicClient, http, type Address } from "viem";
import { sepolia } from "viem/chains";
import type { KernelAccountClient } from "@zerodev/sdk";
const ZERODEV_PROJECT_ID = process.env.NEXT_PUBLIC_ZERODEV_PROJECT_ID!;
const CHAIN = sepolia;
const CHAIN_ID = CHAIN.id;
const BUNDLER_RPC = `https://rpc.zerodev.app/api/v3/${ZERODEV_PROJECT_ID}/chain/${CHAIN_ID}`;
const PAYMASTER_RPC = `https://rpc.zerodev.app/api/v3/${ZERODEV_PROJECT_ID}/chain/${CHAIN_ID}`;
const PUBLIC_RPC = "https://ethereum-sepolia-rpc.publicnode.com";
const ENTRY_POINT = getEntryPoint("0.7");
const KERNEL_VERSION = KERNEL_V3_1;
export function useSmartAccountClient() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const [client, setClient] = useState<KernelAccountClient | null>(null);
const [address, setAddress] = useState<Address | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const initializedForAddress = useRef<string | null>(null);
useEffect(() => {
if (!viemAccount || isViemLoading) return;
if (initializedForAddress.current === viemAccount.address) return;
const initializeClient = async () => {
setIsLoading(true);
setError(null);
try {
const walletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(PUBLIC_RPC),
});
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(PUBLIC_RPC),
});
const ecdsaValidator = await signerToEcdsaValidator(walletClient, {
signer: viemAccount,
entryPoint: ENTRY_POINT,
kernelVersion: KERNEL_VERSION,
});
const kernelAccount = await createKernelAccount(publicClient, {
plugins: { sudo: ecdsaValidator },
entryPoint: ENTRY_POINT,
kernelVersion: KERNEL_VERSION,
});
const paymasterClient = createZeroDevPaymasterClient({
chain: CHAIN,
transport: http(PAYMASTER_RPC),
});
const kernelClient = createKernelAccountClient({
account: kernelAccount,
chain: CHAIN,
bundlerTransport: http(BUNDLER_RPC),
paymaster: {
getPaymasterData: (userOperation) => paymasterClient.sponsorUserOperation({ userOperation }),
},
});
setClient(kernelClient as KernelAccountClient);
setAddress(kernelAccount.address);
initializedForAddress.current = viemAccount.address;
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize"));
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, isViemLoading]);
return { client, address, isLoading: isLoading || isViemLoading, error };
}
Installation
Copy
Ask AI
npm install @gelatonetwork/smartwallet viem
Configuration
useSmartAccountClient.ts
Copy
Ask AI
import { useState, useEffect, useRef } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { accounts, createGelatoSmartWalletClient } from "@gelatonetwork/smartwallet";
import { createWalletClient, createPublicClient, http, type Address } from "viem";
import { sepolia } from "viem/chains";
import type { GelatoSmartWalletClient } from "@gelatonetwork/smartwallet";
const GELATO_API_KEY = process.env.NEXT_PUBLIC_GELATO_API_KEY!;
const CHAIN = sepolia;
export function useSmartAccountClient() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const [client, setClient] = useState<GelatoSmartWalletClient | null>(null);
const [address, setAddress] = useState<Address | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const initializedForAddress = useRef<string | null>(null);
useEffect(() => {
if (!viemAccount || isViemLoading || !GELATO_API_KEY) return;
if (initializedForAddress.current === viemAccount.address) return;
const initializeClient = async () => {
setIsLoading(true);
setError(null);
try {
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(),
});
const kernelAccount = await accounts.kernel({
owner: viemAccount,
client: publicClient,
index: BigInt(0),
eip7702: false,
});
const walletClient = createWalletClient({
account: kernelAccount,
chain: CHAIN,
transport: http(),
});
const smartWalletClient = await createGelatoSmartWalletClient(walletClient, {
apiKey: GELATO_API_KEY,
});
setClient(smartWalletClient);
setAddress(kernelAccount.address);
initializedForAddress.current = viemAccount.address;
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize"));
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, isViemLoading]);
return { client, address, isLoading: isLoading || isViemLoading, error };
}
Installation
Copy
Ask AI
npm install permissionless viem
Configuration
useSmartAccountClient.ts
Copy
Ask AI
import { useState, useEffect, useRef } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { createSmartAccountClient } from "permissionless";
import { toSimpleSmartAccount } from "permissionless/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { createPublicClient, http, type Address } from "viem";
import { sepolia } from "viem/chains";
import { entryPoint07Address } from "viem/account-abstraction";
const PIMLICO_API_KEY = process.env.NEXT_PUBLIC_PIMLICO_API_KEY!;
const CHAIN = sepolia;
const PIMLICO_URL = `https://api.pimlico.io/v2/${CHAIN.id}/rpc?apikey=${PIMLICO_API_KEY}`;
const PUBLIC_RPC = "https://ethereum-sepolia-rpc.publicnode.com";
export function useSmartAccountClient() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const [client, setClient] = useState<any>(null);
const [address, setAddress] = useState<Address | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const initializedForAddress = useRef<string | null>(null);
useEffect(() => {
if (!viemAccount || isViemLoading) return;
if (initializedForAddress.current === viemAccount.address) return;
const initializeClient = async () => {
setIsLoading(true);
setError(null);
try {
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(PUBLIC_RPC),
});
const simpleSmartAccount = await toSimpleSmartAccount({
owner: viemAccount,
client: publicClient,
entryPoint: { address: entryPoint07Address, version: "0.7" },
});
const pimlicoClient = createPimlicoClient({
transport: http(PIMLICO_URL),
entryPoint: { address: entryPoint07Address, version: "0.7" },
});
const smartAccountClient = createSmartAccountClient({
account: simpleSmartAccount,
chain: CHAIN,
bundlerTransport: http(PIMLICO_URL),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => (await pimlicoClient.getUserOperationGasPrice()).fast,
},
});
setClient(smartAccountClient);
setAddress(simpleSmartAccount.address);
initializedForAddress.current = viemAccount.address;
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize"));
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, isViemLoading]);
return { client, address, isLoading: isLoading || isViemLoading, error };
}
Installation
Copy
Ask AI
npm install @biconomy/account viem
Configuration
useSmartAccountClient.ts
Copy
Ask AI
import { useState, useEffect, useRef } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { createSmartAccountClient } from "@biconomy/account";
import { createWalletClient, http, type Address } from "viem";
import { sepolia } from "viem/chains";
const BICONOMY_BUNDLER_API_KEY = process.env.NEXT_PUBLIC_BICONOMY_BUNDLER_KEY!;
const BICONOMY_PAYMASTER_API_KEY = process.env.NEXT_PUBLIC_BICONOMY_PAYMASTER_KEY!;
const CHAIN = sepolia;
const BUNDLER_URL = `https://bundler.biconomy.io/api/v2/${CHAIN.id}/${BICONOMY_BUNDLER_API_KEY}`;
const PAYMASTER_URL = `https://paymaster.biconomy.io/api/v2/${CHAIN.id}/${BICONOMY_PAYMASTER_API_KEY}`;
const PUBLIC_RPC = "https://ethereum-sepolia-rpc.publicnode.com";
export function useSmartAccountClient() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const [client, setClient] = useState<any>(null);
const [address, setAddress] = useState<Address | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const initializedForAddress = useRef<string | null>(null);
useEffect(() => {
if (!viemAccount || isViemLoading) return;
if (initializedForAddress.current === viemAccount.address) return;
const initializeClient = async () => {
setIsLoading(true);
setError(null);
try {
const walletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(PUBLIC_RPC),
});
const smartAccountClient = await createSmartAccountClient({
signer: walletClient,
chainId: CHAIN.id,
bundlerUrl: BUNDLER_URL,
paymasterUrl: PAYMASTER_URL,
});
setClient(smartAccountClient);
setAddress(await smartAccountClient.getAccountAddress());
initializedForAddress.current = viemAccount.address;
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize"));
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, isViemLoading]);
return { client, address, isLoading: isLoading || isViemLoading, error };
}
Installation
Copy
Ask AI
npm install permissionless viem
Configuration
useSmartAccountClient.ts
Copy
Ask AI
import { useState, useEffect, useRef } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { createSmartAccountClient } from "permissionless";
import { toSafeSmartAccount } from "permissionless/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { createWalletClient, createPublicClient, http, type Address } from "viem";
import { sepolia } from "viem/chains";
import { entryPoint07Address } from "viem/account-abstraction";
const PIMLICO_API_KEY = process.env.NEXT_PUBLIC_PIMLICO_API_KEY!;
const CHAIN = sepolia;
const PIMLICO_URL = `https://api.pimlico.io/v2/${CHAIN.id}/rpc?apikey=${PIMLICO_API_KEY}`;
const PUBLIC_RPC = "https://ethereum-sepolia-rpc.publicnode.com";
export function useSmartAccountClient() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const [client, setClient] = useState<any>(null);
const [address, setAddress] = useState<Address | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const initializedForAddress = useRef<string | null>(null);
useEffect(() => {
if (!viemAccount || isViemLoading) return;
if (initializedForAddress.current === viemAccount.address) return;
const initializeClient = async () => {
setIsLoading(true);
setError(null);
try {
const walletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(PUBLIC_RPC),
});
const publicClient = createPublicClient({
chain: CHAIN,
transport: http(PUBLIC_RPC),
});
const safeAccount = await toSafeSmartAccount({
client: publicClient,
owners: [walletClient],
entryPoint: { address: entryPoint07Address, version: "0.7" },
version: "1.4.1",
});
const pimlicoClient = createPimlicoClient({
transport: http(PIMLICO_URL),
entryPoint: { address: entryPoint07Address, version: "0.7" },
});
const smartAccountClient = createSmartAccountClient({
account: safeAccount,
chain: CHAIN,
bundlerTransport: http(PIMLICO_URL),
paymaster: pimlicoClient,
userOperation: {
estimateFeesPerGas: async () => (await pimlicoClient.getUserOperationGasPrice()).fast,
},
});
setClient(smartAccountClient);
setAddress(safeAccount.address);
initializedForAddress.current = viemAccount.address;
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize"));
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, isViemLoading]);
return { client, address, isLoading: isLoading || isViemLoading, error };
}
Installation
Copy
Ask AI
npm install thirdweb viem
Configuration
useSmartAccountClient.ts
Copy
Ask AI
import { useState, useEffect, useRef } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { smartWallet } from "thirdweb/wallets";
import { viemAdapter } from "thirdweb/adapters/viem";
import { createThirdwebClient } from "thirdweb";
import { sepolia } from "thirdweb/chains";
import { createWalletClient, http, type Address } from "viem";
const THIRDWEB_CLIENT_ID = process.env.NEXT_PUBLIC_THIRDWEB_CLIENT_ID!;
const CHAIN = sepolia;
const PUBLIC_RPC = "https://ethereum-sepolia-rpc.publicnode.com";
const thirdwebClient = createThirdwebClient({ clientId: THIRDWEB_CLIENT_ID });
export function useSmartAccountClient() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const [smartAccount, setSmartAccount] = useState<any>(null);
const [address, setAddress] = useState<Address | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const initializedForAddress = useRef<string | null>(null);
useEffect(() => {
if (!viemAccount || isViemLoading) return;
if (initializedForAddress.current === viemAccount.address) return;
const initializeClient = async () => {
setIsLoading(true);
setError(null);
try {
const viemWalletClient = createWalletClient({
account: viemAccount,
chain: CHAIN,
transport: http(PUBLIC_RPC),
});
const personalAccount = viemAdapter.walletClient.fromViem({
walletClient: viemWalletClient,
});
const smartWalletConfig = smartWallet({
chain: CHAIN,
sponsorGas: true,
});
const account = await smartWalletConfig.connect({
client: thirdwebClient,
personalAccount,
});
setSmartAccount(account);
setAddress(account.address as Address);
initializedForAddress.current = viemAccount.address;
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize"));
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, isViemLoading]);
return { client: smartAccount, thirdwebClient, address, isLoading: isLoading || isViemLoading, error };
}
Rhinestone provides cross-chain smart account orchestration with unified balances across chains.
Installation
Copy
Ask AI
npm install @rhinestone/sdk viem
Configuration
useGlobalWallet.ts
Copy
Ask AI
import { useState, useEffect, useCallback } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { RhinestoneSDK } from "@rhinestone/sdk";
import type { Account, Address } from "viem";
const RHINESTONE_API_KEY = process.env.NEXT_PUBLIC_RHINESTONE_API_KEY!;
export function useGlobalWallet() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const [rhinestoneAccount, setRhinestoneAccount] = useState<any>(null);
const [address, setAddress] = useState<Address | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<Error | null>(null);
const initializeAccount = useCallback(async () => {
if (!viemAccount || isViemLoading) return;
setIsLoading(true);
setError(null);
try {
const rhinestone = new RhinestoneSDK({
apiKey: RHINESTONE_API_KEY,
});
const account = await rhinestone.createAccount({
owners: {
type: "ecdsa",
accounts: [viemAccount as Account],
},
});
const accountAddress = account.getAddress();
setRhinestoneAccount(account);
setAddress(accountAddress as Address);
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize"));
} finally {
setIsLoading(false);
}
}, [viemAccount, isViemLoading]);
useEffect(() => {
initializeAccount();
}, [initializeAccount]);
return {
client: rhinestoneAccount,
address,
isLoading: isLoading || isViemLoading,
error,
};
}
Rhinestone accounts support cross-chain operations. Use
rhinestoneAccount.sendTransaction() with sourceChains, targetChain, and tokenRequests for orchestrated cross-chain transactions.