# Examples

## Basic authorization

This will set the EOA code to the [`Simple7702Account`](https://github.com/eth-infinitism/account-abstraction/blob/develop/contracts/accounts/Simple7702Account.sol) implementation.

:::code-group
```ts twoslash [example.ts]
import { walletClient } from "./client.js";

const accountImplementation = "0x4Cd241E8d1510e30b2076397afc7508Ae59C66c9";

const authorization = await walletClient.signAuthorization({
	contractAddress: accountImplementation,
	executor: "self",
});

const hash = await walletClient.sendTransaction({
	authorizationList: [authorization],
	data: "0x",
	to: walletClient.account.address,
});
console.log(`Delegated at tx ${hash}`);

```

```ts twoslash [client.ts] filename="client.ts"
import {
	http,
	type Account,
	type Chain,
	type Transport,
	type WalletClient,
	createWalletClient,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";

const privateKey = "0x…";
const account = privateKeyToAccount(privateKey);
console.log(`Account address = ${account.address}`);
export const walletClient = createWalletClient({
	account,
	chain: sepolia,
	transport: http(),
}) as WalletClient<Transport, Chain, Account>;
```
:::

## Transaction batching

Once the EOA code is delegated to a smart account implementation, you can start making batched transactions.

:::code-group
```ts twoslash [example.ts]
import { zeroAddress } from "viem";

import accountAbi from "./abi.js";
import { account, walletClient } from "./client.js";

const hash = await walletClient.writeContract({
	abi: accountAbi,
	address: account.address,
	functionName: "executeBatch",
	args: [
		[
			{
				target: zeroAddress,
				value: 1n,
				data: "0x",
			},
			{
				target: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
				value: 0n,
				data: "0xdeadbeef",
			},
		],
	],
});
console.log(`Transaction hash ${hash}`);

```

```ts twoslash [client.ts] filename="client.ts"
import {
	http,
	type Account,
	type Chain,
	type Transport,
	type WalletClient,
	createWalletClient,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";

const privateKey =
	"0x…";
const account = privateKeyToAccount(privateKey);
console.log(`Account address = ${account.address}`);
const walletClient = createWalletClient({
	account,
	chain: sepolia,
	transport: http(),
}) as WalletClient<Transport, Chain, Account>;

export { account, walletClient };
```

```ts twoslash [abi.ts] filename="abi.ts"
const abi = [
	{
		inputs: [],
		name: "ECDSAInvalidSignature",
		type: "error",
	},
	{
		inputs: [
			{
				internalType: "uint256",
				name: "length",
				type: "uint256",
			},
		],
		name: "ECDSAInvalidSignatureLength",
		type: "error",
	},
	{
		inputs: [
			{
				internalType: "bytes32",
				name: "s",
				type: "bytes32",
			},
		],
		name: "ECDSAInvalidSignatureS",
		type: "error",
	},
	{
		inputs: [
			{
				internalType: "uint256",
				name: "index",
				type: "uint256",
			},
			{
				internalType: "bytes",
				name: "error",
				type: "bytes",
			},
		],
		name: "ExecuteError",
		type: "error",
	},
	{
		stateMutability: "payable",
		type: "fallback",
	},
	{
		inputs: [],
		name: "entryPoint",
		outputs: [
			{
				internalType: "contract IEntryPoint",
				name: "",
				type: "address",
			},
		],
		stateMutability: "pure",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "address",
				name: "target",
				type: "address",
			},
			{
				internalType: "uint256",
				name: "value",
				type: "uint256",
			},
			{
				internalType: "bytes",
				name: "data",
				type: "bytes",
			},
		],
		name: "execute",
		outputs: [],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [
			{
				components: [
					{
						internalType: "address",
						name: "target",
						type: "address",
					},
					{
						internalType: "uint256",
						name: "value",
						type: "uint256",
					},
					{
						internalType: "bytes",
						name: "data",
						type: "bytes",
					},
				],
				internalType: "struct BaseAccount.Call[]",
				name: "calls",
				type: "tuple[]",
			},
		],
		name: "executeBatch",
		outputs: [],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [],
		name: "getNonce",
		outputs: [
			{
				internalType: "uint256",
				name: "",
				type: "uint256",
			},
		],
		stateMutability: "view",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "bytes32",
				name: "hash",
				type: "bytes32",
			},
			{
				internalType: "bytes",
				name: "signature",
				type: "bytes",
			},
		],
		name: "isValidSignature",
		outputs: [
			{
				internalType: "bytes4",
				name: "magicValue",
				type: "bytes4",
			},
		],
		stateMutability: "view",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "uint256[]",
				name: "",
				type: "uint256[]",
			},
			{
				internalType: "uint256[]",
				name: "",
				type: "uint256[]",
			},
			{
				internalType: "bytes",
				name: "",
				type: "bytes",
			},
		],
		name: "onERC1155BatchReceived",
		outputs: [
			{
				internalType: "bytes4",
				name: "",
				type: "bytes4",
			},
		],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "uint256",
				name: "",
				type: "uint256",
			},
			{
				internalType: "uint256",
				name: "",
				type: "uint256",
			},
			{
				internalType: "bytes",
				name: "",
				type: "bytes",
			},
		],
		name: "onERC1155Received",
		outputs: [
			{
				internalType: "bytes4",
				name: "",
				type: "bytes4",
			},
		],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "uint256",
				name: "",
				type: "uint256",
			},
			{
				internalType: "bytes",
				name: "",
				type: "bytes",
			},
		],
		name: "onERC721Received",
		outputs: [
			{
				internalType: "bytes4",
				name: "",
				type: "bytes4",
			},
		],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "bytes4",
				name: "id",
				type: "bytes4",
			},
		],
		name: "supportsInterface",
		outputs: [
			{
				internalType: "bool",
				name: "",
				type: "bool",
			},
		],
		stateMutability: "pure",
		type: "function",
	},
	{
		inputs: [
			{
				components: [
					{
						internalType: "address",
						name: "sender",
						type: "address",
					},
					{
						internalType: "uint256",
						name: "nonce",
						type: "uint256",
					},
					{
						internalType: "bytes",
						name: "initCode",
						type: "bytes",
					},
					{
						internalType: "bytes",
						name: "callData",
						type: "bytes",
					},
					{
						internalType: "bytes32",
						name: "accountGasLimits",
						type: "bytes32",
					},
					{
						internalType: "uint256",
						name: "preVerificationGas",
						type: "uint256",
					},
					{
						internalType: "bytes32",
						name: "gasFees",
						type: "bytes32",
					},
					{
						internalType: "bytes",
						name: "paymasterAndData",
						type: "bytes",
					},
					{
						internalType: "bytes",
						name: "signature",
						type: "bytes",
					},
				],
				internalType: "struct PackedUserOperation",
				name: "userOp",
				type: "tuple",
			},
			{
				internalType: "bytes32",
				name: "userOpHash",
				type: "bytes32",
			},
			{
				internalType: "uint256",
				name: "missingAccountFunds",
				type: "uint256",
			},
		],
		name: "validateUserOp",
		outputs: [
			{
				internalType: "uint256",
				name: "validationData",
				type: "uint256",
			},
		],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		stateMutability: "payable",
		type: "receive",
	},
] as const;

export default abi;
```
:::

## Sending a user operation

Signs and sends a UserOp using the ERC-4337 flow. Assumes the account is already initialized, and the EOA is the owner of the account.

This way, you can also sponsor the transactions by using a paymaster.

Note that the process is exactly the same as when using a regular smart account. To minimize the code surface, you can use a convenience library like [permissionless.js](https://github.com/pimlicolabs/permissionless.js).

:::code-group
```ts twoslash [example.ts]
// @noErrors
// Vocs only resolves code-group imports one level deep, so `abi.ts` (imported
// by `account.ts`, not by this file) is missing from the checked snippet.
import { zeroAddress } from "viem";

import { smartAccount } from "./account.js";
import { bundlerClient } from "./client.js";

const userOpHash = await bundlerClient.sendUserOperation({
	account: smartAccount,
	calls: [
		{
			to: zeroAddress,
			value: 1n,
		},
		{
			to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
			data: "0xdeadbeef",
		},
	],
});
console.log(`User operation sent ${userOpHash}`);
```

```ts twoslash [account.ts] filename="account.ts"
import { decodeFunctionData, encodeFunctionData } from "viem";
import {
	entryPoint08Abi,
	entryPoint08Address,
	getUserOperationHash,
	toSmartAccount,
} from "viem/account-abstraction";
import { privateKeyToAccount } from "viem/accounts";

import accountAbi from "./abi.js";
import { chain, publicClient } from "./client.js";

const ECDSA_MOCK_SIGNATURE =
	"0x81d4b4981670cb18f99f0b4a66446df1bf5b204d24cfcb659bf38ba27a4359b5711649ec2423c5e1247245eba2964679b6a1dbb85c992ae40b9b00c6935b02ff1b";

const privateKey = '0x…';
const account = privateKeyToAccount(privateKey);
console.log(`Account address = ${account.address}`);

const smartAccount = await toSmartAccount({
	client: publicClient,
	entryPoint: {
		abi: entryPoint08Abi,
		address: entryPoint08Address,
		version: "0.8",
	},
	async decodeCalls(data) {
		const calls = decodeFunctionData({
			abi: accountAbi,
			data,
		});
		if (calls.functionName === "execute") {
			return [
				{
					to: calls.args[0],
					value: calls.args[1],
					data: calls.args[2],
				},
			];
		}
		if (calls.functionName === "executeBatch") {
			return calls.args[0].map((arg) => ({
				to: arg.target,
				value: arg.value,
				data: arg.data,
			}));
		}
		throw new Error(`Unable to decode data: ${data}`);
	},
	async encodeCalls(calls) {
		if (calls.length === 1) {
			const call = calls[0];
			return encodeFunctionData({
				abi: accountAbi,
				functionName: "execute",
				args: [call.to, call.value || 0n, call.data || "0x"],
			});
		}
		return encodeFunctionData({
			abi: accountAbi,
			functionName: "executeBatch",
			args: [
				calls.map((call) => ({
					target: call.to,
					value: call.value || 0n,
					data: call.data || "0x",
				})),
			],
		});
	},
	async getAddress() {
		return account.address;
	},
	async getFactoryArgs() {
		throw new Error("Factory arguments not available");
	},
	async getNonce(args) {
		const nonce = await publicClient.readContract({
			address: entryPoint08Address,
			abi: entryPoint08Abi,
			functionName: "getNonce",
			args: [account.address, args?.key || 0n],
		});
		return nonce;
	},
	async getStubSignature() {
		return ECDSA_MOCK_SIGNATURE;
	},
	async signMessage(message) {
		return account.signMessage(message);
	},
	async signTypedData(typedData) {
		return account.signTypedData(typedData);
	},
	async signUserOperation(userOperation) {
		const hash = getUserOperationHash({
			chainId: chain.id,
			entryPointAddress: entryPoint08Address,
			entryPointVersion: "0.8",
			userOperation: {
				...userOperation,
				sender: account.address,
			},
		});
		return account.sign({
			hash,
		});
	},
});

export { smartAccount };
```

```ts twoslash [client.ts] filename="client.ts"
import { http, createPublicClient } from "viem";
import { createBundlerClient } from "viem/account-abstraction";
import { sepolia } from "viem/chains";

const chain = sepolia;

const publicClient = createPublicClient({
	chain,
	transport: http(),
});

const bundlerClient = createBundlerClient({
	client: publicClient,
	transport: http(`https://public.pimlico.io/v2/${chain.id}/rpc`),
});

export { chain, publicClient, bundlerClient };

```

```ts twoslash [abi.ts] filename="abi.ts"
const abi = [
	{
		inputs: [],
		name: "ECDSAInvalidSignature",
		type: "error",
	},
	{
		inputs: [
			{
				internalType: "uint256",
				name: "length",
				type: "uint256",
			},
		],
		name: "ECDSAInvalidSignatureLength",
		type: "error",
	},
	{
		inputs: [
			{
				internalType: "bytes32",
				name: "s",
				type: "bytes32",
			},
		],
		name: "ECDSAInvalidSignatureS",
		type: "error",
	},
	{
		inputs: [
			{
				internalType: "uint256",
				name: "index",
				type: "uint256",
			},
			{
				internalType: "bytes",
				name: "error",
				type: "bytes",
			},
		],
		name: "ExecuteError",
		type: "error",
	},
	{
		stateMutability: "payable",
		type: "fallback",
	},
	{
		inputs: [],
		name: "entryPoint",
		outputs: [
			{
				internalType: "contract IEntryPoint",
				name: "",
				type: "address",
			},
		],
		stateMutability: "pure",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "address",
				name: "target",
				type: "address",
			},
			{
				internalType: "uint256",
				name: "value",
				type: "uint256",
			},
			{
				internalType: "bytes",
				name: "data",
				type: "bytes",
			},
		],
		name: "execute",
		outputs: [],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [
			{
				components: [
					{
						internalType: "address",
						name: "target",
						type: "address",
					},
					{
						internalType: "uint256",
						name: "value",
						type: "uint256",
					},
					{
						internalType: "bytes",
						name: "data",
						type: "bytes",
					},
				],
				internalType: "struct BaseAccount.Call[]",
				name: "calls",
				type: "tuple[]",
			},
		],
		name: "executeBatch",
		outputs: [],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [],
		name: "getNonce",
		outputs: [
			{
				internalType: "uint256",
				name: "",
				type: "uint256",
			},
		],
		stateMutability: "view",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "bytes32",
				name: "hash",
				type: "bytes32",
			},
			{
				internalType: "bytes",
				name: "signature",
				type: "bytes",
			},
		],
		name: "isValidSignature",
		outputs: [
			{
				internalType: "bytes4",
				name: "magicValue",
				type: "bytes4",
			},
		],
		stateMutability: "view",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "uint256[]",
				name: "",
				type: "uint256[]",
			},
			{
				internalType: "uint256[]",
				name: "",
				type: "uint256[]",
			},
			{
				internalType: "bytes",
				name: "",
				type: "bytes",
			},
		],
		name: "onERC1155BatchReceived",
		outputs: [
			{
				internalType: "bytes4",
				name: "",
				type: "bytes4",
			},
		],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "uint256",
				name: "",
				type: "uint256",
			},
			{
				internalType: "uint256",
				name: "",
				type: "uint256",
			},
			{
				internalType: "bytes",
				name: "",
				type: "bytes",
			},
		],
		name: "onERC1155Received",
		outputs: [
			{
				internalType: "bytes4",
				name: "",
				type: "bytes4",
			},
		],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "address",
				name: "",
				type: "address",
			},
			{
				internalType: "uint256",
				name: "",
				type: "uint256",
			},
			{
				internalType: "bytes",
				name: "",
				type: "bytes",
			},
		],
		name: "onERC721Received",
		outputs: [
			{
				internalType: "bytes4",
				name: "",
				type: "bytes4",
			},
		],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		inputs: [
			{
				internalType: "bytes4",
				name: "id",
				type: "bytes4",
			},
		],
		name: "supportsInterface",
		outputs: [
			{
				internalType: "bool",
				name: "",
				type: "bool",
			},
		],
		stateMutability: "pure",
		type: "function",
	},
	{
		inputs: [
			{
				components: [
					{
						internalType: "address",
						name: "sender",
						type: "address",
					},
					{
						internalType: "uint256",
						name: "nonce",
						type: "uint256",
					},
					{
						internalType: "bytes",
						name: "initCode",
						type: "bytes",
					},
					{
						internalType: "bytes",
						name: "callData",
						type: "bytes",
					},
					{
						internalType: "bytes32",
						name: "accountGasLimits",
						type: "bytes32",
					},
					{
						internalType: "uint256",
						name: "preVerificationGas",
						type: "uint256",
					},
					{
						internalType: "bytes32",
						name: "gasFees",
						type: "bytes32",
					},
					{
						internalType: "bytes",
						name: "paymasterAndData",
						type: "bytes",
					},
					{
						internalType: "bytes",
						name: "signature",
						type: "bytes",
					},
				],
				internalType: "struct PackedUserOperation",
				name: "userOp",
				type: "tuple",
			},
			{
				internalType: "bytes32",
				name: "userOpHash",
				type: "bytes32",
			},
			{
				internalType: "uint256",
				name: "missingAccountFunds",
				type: "uint256",
			},
		],
		name: "validateUserOp",
		outputs: [
			{
				internalType: "uint256",
				name: "validationData",
				type: "uint256",
			},
		],
		stateMutability: "nonpayable",
		type: "function",
	},
	{
		stateMutability: "payable",
		type: "receive",
	},
] as const;

export default abi;
```
:::
