EIP-7702 requires chain support. Currently supported on testnets and select mainnets. Check with your provider for chain availability.
Prerequisites
Para Setup Required
Provider Setup
- Alchemy
- ZeroDev
- Gelato
- Porto
Alchemy supports 7702 via the
mode: "7702" parameter using the same Account Kit SDK.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({
mode: "7702",
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 };
}
The only difference from 4337 is
mode: "7702". The smart account address will be your EOA address.ZeroDev provides dedicated 7702 functions for native account abstraction.
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 { createZeroDevPaymasterClient } from "@zerodev/sdk";
import { getEntryPoint, KERNEL_V3_3 } from "@zerodev/sdk/constants";
import { create7702KernelAccount, create7702KernelAccountClient } from "@zerodev/ecdsa-validator";
import { 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_3;
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 publicClient = createPublicClient({
chain: CHAIN,
transport: http(PUBLIC_RPC),
});
const kernelAccount = await create7702KernelAccount(publicClient, {
signer: viemAccount,
entryPoint: ENTRY_POINT,
kernelVersion: KERNEL_VERSION,
});
const paymasterClient = createZeroDevPaymasterClient({
chain: CHAIN,
transport: http(PAYMASTER_RPC),
});
const kernelClient = create7702KernelAccountClient({
account: kernelAccount,
chain: CHAIN,
bundlerTransport: http(BUNDLER_RPC),
paymaster: paymasterClient,
client: publicClient,
});
setClient(kernelClient as KernelAccountClient);
setAddress(kernelAccount.address);
initializedForAddress.current = viemAccount.address;
} catch (err) {
setError(err instanceof Error ? err : new Error("Failed to initialize 7702 account"));
} finally {
setIsLoading(false);
}
};
initializeClient();
}, [viemAccount, isViemLoading]);
return { client, address, isLoading: isLoading || isViemLoading, error };
}
Uses
create7702KernelAccount and create7702KernelAccountClient instead of the standard 4337 functions.Gelato provides native 7702 support via the
gelato() account type.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 { gelato } from "@gelatonetwork/smartwallet/accounts";
import { 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 smartAccount = await gelato({
owner: viemAccount,
client: publicClient,
});
const walletClient = createWalletClient({
account: smartAccount,
chain: CHAIN,
transport: http(),
});
const smartWalletClient = await createGelatoSmartWalletClient(walletClient, {
apiKey: GELATO_API_KEY,
});
setClient(smartWalletClient);
setAddress(smartAccount.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 };
}
Uses
gelato() instead of accounts.kernel(). The address remains your EOA address.Porto is a 7702-native smart account provider with unique upgrade flow.
Installation
Copy
Ask AI
npm install porto viem
Configuration
usePortoAccount.ts
Copy
Ask AI
import { useState, useCallback, useMemo, useEffect } from "react";
import { useViemAccount } from "@getpara/react-sdk/evm";
import { useAccount } from "@getpara/react-sdk";
import { Chains } from "porto";
import { Account, Key, RelayActions } from "porto/viem";
import { createClient, http, type Hex } from "viem";
export function usePortoAccount() {
const { viemAccount, isLoading: isViemLoading } = useViemAccount();
const { isConnected } = useAccount();
const [portoAccount, setPortoAccount] = useState<any>(null);
const [isUpgrading, setIsUpgrading] = useState(false);
const [isCheckingStatus, setIsCheckingStatus] = useState(false);
const [error, setError] = useState<string | null>(null);
const portoClient = useMemo(
() =>
createClient({
chain: Chains.baseSepolia,
transport: http("https://rpc.porto.sh"),
}),
[]
);
const signRawHash = useCallback(
async (hash: Hex): Promise<Hex> => {
if (!viemAccount?.sign) throw new Error("Viem account not available");
return viemAccount.sign({ hash });
},
[viemAccount]
);
useEffect(() => {
async function checkPortoStatus() {
if (!viemAccount?.address || !isConnected) return;
setIsCheckingStatus(true);
try {
const tempAccount = Account.from({ address: viemAccount.address });
const keys = await RelayActions.getKeys(portoClient, { account: tempAccount });
if (keys.length > 0) {
const account = Account.from({
address: viemAccount.address,
keys: [...keys],
async sign({ hash }) {
return signRawHash(hash as Hex);
},
});
setPortoAccount(account);
}
} catch {
// Account not upgraded yet
} finally {
setIsCheckingStatus(false);
}
}
checkPortoStatus();
}, [viemAccount?.address, isConnected, portoClient, signRawHash]);
const upgradeToPorto = useCallback(async () => {
if (!viemAccount || !isConnected) {
setError("Wallet not connected");
return;
}
setIsUpgrading(true);
setError(null);
try {
const customAccount = Account.from({
address: viemAccount.address,
async sign({ hash }) {
return signRawHash(hash as Hex);
},
});
const adminKey = Key.createSecp256k1({ role: "admin" });
const prepared = await RelayActions.prepareUpgradeAccount(portoClient, {
address: customAccount.address,
authorizeKeys: [adminKey],
});
const signatures = {
auth: await signRawHash(prepared.digests.auth as Hex),
exec: await signRawHash(prepared.digests.exec as Hex),
};
const upgradedAccount = await RelayActions.upgradeAccount(portoClient, {
...prepared,
signatures,
});
setPortoAccount(upgradedAccount);
} catch (err) {
setError(err instanceof Error ? err.message : "Upgrade failed");
} finally {
setIsUpgrading(false);
}
}, [viemAccount, isConnected, portoClient, signRawHash]);
return {
viemAccount,
portoAccount,
address: viemAccount?.address,
isViemLoading,
isUpgrading,
isCheckingStatus,
error,
upgradeToPorto,
isConnected,
};
}
Porto requires an explicit upgrade step. The user’s EOA address becomes a 7702 smart account after upgrade.
7702 vs 4337 Key Differences
| Aspect | EIP-7702 | EIP-4337 |
|---|---|---|
| Address | Same as EOA | New smart contract |
| Setup | mode: "7702" or dedicated functions | Standard setup |
| Chain Support | Requires chain support | All EVM chains |
| Use Case | Keep existing address | Dedicated smart wallet |