Using EIP-7702

Upgrade your user’s accounts to Smart Wallets using EIP-7702. This gives users access to all of the capabilities of Smart Wallets including gas sponsorship, batching, and more.

How it works

EIP-7702 enables EOAs (Externally Owned Accounts) to delegate control to smart wallets that can execute code directly from their addresses. Users simply sign an authorization payload to delegate, then their account can function as a smart wallet with access to all of the user’s assets. Wallet APIs will prompt the user to sign this authorization when the delegation is missing on the chain they’re interacting with.

Prerequisites

Implementation

Required SDK version: ^v4.61.0

To use Smart Wallets in EIP-7702 mode, you must specify the mode when constructing the smart account client with useSmartAccountClient. This will allow the authorized EOA to act as a smart wallet with the same address as the EOA. The useSendCalls hook will sign the required delegation payload the first time that a user interacts with a new chain. This payload is then relayed on chain with the user’s first transaction.

Prerequisites:

See the useSendCalls SDK reference for parameter descriptions.

sendCalls.tsx
1import { useSendCalls, useSmartAccountClient } from "@account-kit/react";
2
3export default function SendCalls() {
4 const { client } = useSmartAccountClient({
5 accountParams: {
6 mode: "7702",
7 },
8 });
9 const { sendCallsAsync } = useSendCalls({ client });
10
11 const handleSend = async () => {
12 if (!client) {
13 throw new Error("Smart account client not connected");
14 }
15
16 try {
17 const { ids } = await sendCallsAsync({
18 calls: [
19 {
20 to: "0x0000000000000000000000000000000000000000",
21 value: "0x00",
22 data: "0x",
23 },
24 ],
25 });
26
27 console.log("Transaction sent with ID:", ids[0]);
28 } catch (error) {
29 console.error(error);
30 }
31 };
32
33 return <button onClick={handleSend}>Click to Send</button>;
34}

Advanced

EIP-7702 is particularly useful when you have existing wallets that are being used as EOAs, but want access to smart wallet capabilities while allowing users to keep their existing address and avoid transferring their assets. With EIP-7702, you can upgrade your users’ EOAs directly to a Smart Wallet at their current address.

See a full list of recommendations here.

The EIP-7702 capability works with the prepare calls function in a similar way to its usage in send calls. However, you are required to handle the authorization signature request in your code’s logic.

When a delegation is required the response to prepare calls will return an array of signature requests:

  1. An authorization signature request
  2. A user operation signature request

Both of these requests need to be signed and included in the sendPreparedCalls request. sendPreparedCalls takes an array of signed calls for this purpose. These signatures are combined into a single transaction that is relayed onchain. Once the delegation is signed, as long as the user doesn’t delegate to another account, subsequent requests will only contain the user operation signature request.

Signing an authorization signature request requires logic specific to the wallet being used. Examples:

Most external wallet implementations, including browser wallets, will block requests to sign authorizations. This is done for security reasons. To use EIP-7702 ensure that your user’s wallets support signing authorizations.

The eip7702Auth capability supports the interface defined in ERC-7902. However, it currently only supports a single delegation address: 0x69007702764179f14F51cdce752f4f775d74E139. All other addresses will be rejected. It’s recommended to use eip7702Auth: true to avoid the extra complexity.

Once your account is delegated, it will remain delegated until the delegation is replaced or removed. We currently only support relaying delegations for Modular Account v2. If you wish to replace or remove this delegation, you’ll need to relay the delegation yourself or use a third party service. Viem has a guide for this here.

To reset an account back to a pure EOA with no delegation, delegate the account to 0x0000000000000000000000000000000000000000 as defined in EIP-7702.

If you have an existing custom signer or another third-party embedded wallet provider, you can upgrade your embedded EOAs to our smart wallets by connecting your existing signer. This will allow you to use EIP-7702 to get features like gas sponsorship, batching, and more.

Requirement: Your signer or embedded wallet provider must support signing EIP-7702 authorizations in order to delegate to a smart account.

To bring your own signer, you’ll create a SmartAccountSigner that implements signAuthorization. See the details for the interface requirements here.

For example, you can upgrade an existing Privy embedded EOA by extending a Wallet Client to use the sign7702Authorization action exposed by Privy. See full example code here.

The bulk of the logic happens in use-embedded-smart-wallet.ts. In this react hook, a Privy embedded wallet is wrapped in a WalletClientSigner that supports signAuthorization. This signer is then passed to createSmartWalletClient to construct a client for sending transactions.

import { 
type Address = `0x${string}`
Address
,
type Authorization<uint32 = number, signed extends boolean = false> = { address: Address; chainId: uint32; nonce: uint32; } & (signed extends true ? Signature<uint32> : ExactPartial<Signature<uint32>>)
Authorization
,
function createWalletClient<transport extends Transport, chain extends Chain | undefined = undefined, accountOrAddress extends Account | Address | undefined = undefined, rpcSchema extends RpcSchema | undefined = undefined>(parameters: WalletClientConfig<transport, chain, accountOrAddress, rpcSchema>): WalletClient<transport, chain, ParseAccount<accountOrAddress>, rpcSchema>

Creates a Wallet Client with a given Transport configured for a Chain.

Docs: https://viem.sh/docs/clients/wallet

A Wallet Client is an interface to interact with Ethereum Account(s) and provides the ability to retrieve accounts, execute transactions, sign messages, etc. through Wallet Actions.

The Wallet Client supports signing over: JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc). Local Accounts (e.g. private key/mnemonic wallets).

createWalletClient
,
function custom<provider extends EthereumProvider>(provider: provider, config?: CustomTransportConfig): CustomTransport
custom
} from "viem";
import {
import useSign7702Authorization
useSign7702Authorization
} from "@privy-io/react-auth";
import {
type AuthorizationRequest<uint32 = number> = OneOf<{ address: Address; } | { contractAddress: Address; }> & { chainId: uint32; nonce: uint32; }
AuthorizationRequest
,
class WalletClientSigner

Represents a wallet client signer for smart accounts, providing methods to get the address, sign messages, and sign typed data.

WalletClientSigner
} from "@aa-sdk/core";
import {
const sepolia: Chain
sepolia
,
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
} from "@account-kit/infra";
import {
function useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void

Accepts a function that contains imperative, possibly effectful code.

useEffect
,
function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)

Returns a stateful value, and a function to update it.

useState
} from "react";
import {
function createSmartWalletClient<TAccount extends Address | undefined = undefined>(params: SmartWalletClientParams<TAccount>): SmartWalletClient<TAccount>

Creates a smart wallet client that can be used to interact with a smart account.

createSmartWalletClient
,
type SmartWalletClient<TAccount extends Address | undefined = `0x${string}` | undefined> = Client_Base<Transport, Chain, JsonRpcAccount<`0x${string}`> | undefined, WalletServerViemRpcSchema> & { policyIds?: string[]; } & { internal: InternalState; } & { extend: <const client extends { [x: string]: unknown; account?: undefined; batch?: undefined; cacheTime?: undefined; ccipRead?: undefined; chain?: undefined; experimental_blockTag?: undefined; key?: undefined; name?: undefined; pollingInterval?: undefined; request?: undefined; transport?: undefined; type?: undefined; uid?: undefined; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; } & SmartWalletActions<...>
SmartWalletClient
,
} from "@account-kit/wallet-client"; import {
any
ConnectedWallet
as
import PrivyWallet
PrivyWallet
} from "@privy-io/react-auth";
/\*_ Creates an Alchemy Smart Wallet client for an embedded Privy wallet using EIP-7702. _/; export const
const useSmartEmbeddedWallet: ({ embeddedWallet, }: { embeddedWallet: PrivyWallet; }) => { client: SmartWalletClient | undefined; }
useSmartEmbeddedWallet
= ({
embeddedWallet: PrivyWallet
embeddedWallet
,
}: {
embeddedWallet: PrivyWallet
embeddedWallet
:
import PrivyWallet
PrivyWallet
;
}) => { const {
const signAuthorization: any
signAuthorization
} =
import useSign7702Authorization
useSign7702Authorization
();
const [
const client: SmartWalletClient | undefined
client
,
const setClient: React.Dispatch<React.SetStateAction<SmartWalletClient | undefined>>
setClient
] =
useState<SmartWalletClient>(): [SmartWalletClient | undefined, React.Dispatch<React.SetStateAction<SmartWalletClient | undefined>>] (+1 overload)

Returns a stateful value, and a function to update it.

useState
<
type SmartWalletClient<TAccount extends Address | undefined = `0x${string}` | undefined> = Client_Base<Transport, Chain, JsonRpcAccount<`0x${string}`> | undefined, WalletServerViemRpcSchema> & { policyIds?: string[]; } & { internal: InternalState; } & { extend: <const client extends { [x: string]: unknown; account?: undefined; batch?: undefined; cacheTime?: undefined; ccipRead?: undefined; chain?: undefined; experimental_blockTag?: undefined; key?: undefined; name?: undefined; pollingInterval?: undefined; request?: undefined; transport?: undefined; type?: undefined; uid?: undefined; } & ExactPartial<...>>(fn: (client: Client<...>) => client) => Client<...>; } & SmartWalletActions<...>
SmartWalletClient
>();
function useEffect(effect: React.EffectCallback, deps?: React.DependencyList): void

Accepts a function that contains imperative, possibly effectful code.

useEffect
(() => {
const
const apiKey: string | undefined
apiKey
=
var process: NodeJS.Process
process
.
NodeJS.Process.env: NodeJS.ProcessEnv

The process.env property returns an object containing the user environment. See environ(7).

An example of this object looks like:

jsTERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node'

It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work:

bash node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo

While the following will:


env.foo = 'bar'; console.log(env.foo); ```

Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean.

```js import env from 'node:process';

env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ```

Use `delete` to delete a property from `process.env`.

```js import env from 'node:process';

env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ```

On Windows operating systems, environment variables are case-insensitive.

```js import env from 'node:process';

env.TEST = 1; console.log(env.test); // => 1 ```

Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
env
.
string | undefined
NEXT_PUBLIC_ALCHEMY_API_KEY
;
if (!
const apiKey: string | undefined
apiKey
) {
throw new
var Error: ErrorConstructor new (message?: string) => Error
Error
("Missing NEXT_PUBLIC_ALCHEMY_API_KEY");
} (async () => { const
const provider: any
provider
= await
embeddedWallet: PrivyWallet
embeddedWallet
.
any
getEthereumProvider
();
const
const baseSigner: WalletClientSigner
baseSigner
= new
new WalletClientSigner(client: WalletClient, signerType: string): WalletClientSigner

Initializes a signer with a given wallet client and signer type.

WalletClientSigner
(
createWalletClient<CustomTransport, Chain, `0x${string}`, []>(parameters: { account?: `0x${string}` | Account | undefined; cacheTime?: number | undefined | undefined; ccipRead?: { request?: (parameters: CcipRequestParameters) => Promise<CcipRequestReturnType>; } | false | undefined | undefined; ... 5 more ...; transport: CustomTransport; }): { ...; }

Creates a Wallet Client with a given Transport configured for a Chain.

Docs: https://viem.sh/docs/clients/wallet

A Wallet Client is an interface to interact with Ethereum Account(s) and provides the ability to retrieve accounts, execute transactions, sign messages, etc. through Wallet Actions.

The Wallet Client supports signing over: JSON-RPC Accounts (e.g. Browser Extension Wallets, WalletConnect, etc). Local Accounts (e.g. private key/mnemonic wallets).

createWalletClient
({
account?: `0x${string}` | Account | undefined

The Account to use for the Client. This will be used for Actions that require an account as an argument.

account
:
embeddedWallet: PrivyWallet
embeddedWallet
.
any
address
as
type Address = `0x${string}`
Address
,
chain?: Chain | undefined

Chain for the client.

chain
:
const sepolia: Chain
sepolia
,
transport: CustomTransport

The RPC transport

transport
:
custom<any>(provider: any, config?: CustomTransportConfig): CustomTransport
custom
(
const provider: any
provider
),
}), "privy", ); const
const signer: { signAuthorization: (unsignedAuth: AuthorizationRequest<number>) => Promise<Authorization<number, true>>; signerType: string; inner: WalletClient; getAddress: () => Promise<`0x${string}`>; signMessage: (message: SignableMessage) => Promise<`0x${string}`>; signTypedData: <const TTypedData extends TypedData | Record<string, unknown>, TPrimaryType extends keyof TTypedData | "EIP712Domain" | string = string>(typedData: TypedDataDefinition<TTypedData, TPrimaryType>) => Promise<Hex>; }
signer
= {
...
const baseSigner: WalletClientSigner
baseSigner
,
signAuthorization: (unsignedAuth: AuthorizationRequest<number>) => Promise<Authorization<number, true>>
signAuthorization
: async (
unsignedAuth: AuthorizationRequest<number>
unsignedAuth
:
type AuthorizationRequest<uint32 = number> = OneOf<{ address: Address; } | { contractAddress: Address; }> & { chainId: uint32; nonce: uint32; }
AuthorizationRequest
<number>,
):
interface Promise<T>

Represents the completion of an asynchronous operation

Promise
<
type Authorization<uint32 = number, signed extends boolean = false> = { address: Address; chainId: uint32; nonce: uint32; } & (signed extends true ? Signature<uint32> : ExactPartial<Signature<uint32>>)
Authorization
<number, true>> => {
const
const signature: any
signature
= await
const signAuthorization: any
signAuthorization
({
...
unsignedAuth: AuthorizationRequest<number>
unsignedAuth
,
contractAddress: `0x${string}`
contractAddress
:
unsignedAuth: AuthorizationRequest<number>
unsignedAuth
.
address?: `0x${string}` | undefined
address
??
unsignedAuth: { contractAddress: Address; address?: undefined; } & { chainId: number; nonce: number; }
unsignedAuth
.
contractAddress: `0x${string}`
contractAddress
,
}); return { ...
unsignedAuth: AuthorizationRequest<number>
unsignedAuth
,
...
const signature: any
signature
,
}; }, }; const
const client: SmartWalletClient<undefined>
client
=
createSmartWalletClient<undefined>(params: SmartWalletClientParams<undefined>): SmartWalletClient<undefined>

Creates a smart wallet client that can be used to interact with a smart account.

createSmartWalletClient
({
chain: Chain
chain
:
const sepolia: Chain
sepolia
,
transport: AlchemyTransport
transport
:
function alchemy(config: AlchemyTransportConfig): AlchemyTransport

Creates an Alchemy transport with the specified configuration options. When sending all traffic to Alchemy, you must pass in one of rpcUrl, apiKey, or jwt. If you want to send Bundler and Paymaster traffic to Alchemy and Node traffic to a different RPC, you must pass in alchemyConnection and nodeRpcUrl.

alchemy
({
apiKey: string
apiKey
,
}),
signer: SmartAccountSigner<any>
signer
,
policyId?: string | undefined
policyId
:
var process: NodeJS.Process
process
.
NodeJS.Process.env: NodeJS.ProcessEnv

The process.env property returns an object containing the user environment. See environ(7).

An example of this object looks like:

jsTERM: 'xterm-256color', SHELL: '/usr/local/bin/bash', USER: 'maciej', PATH: '~/.bin/:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin', PWD: '/Users/maciej', EDITOR: 'vim', SHLVL: '1', HOME: '/Users/maciej', LOGNAME: 'maciej', _: '/usr/local/bin/node'

It is possible to modify this object, but such modifications will not be reflected outside the Node.js process, or (unless explicitly requested) to other Worker threads. In other words, the following example would not work:

bash node -e 'process.env.foo = "bar"' &#x26;&#x26; echo $foo

While the following will:


env.foo = 'bar'; console.log(env.foo); ```

Assigning a property on `process.env` will implicitly convert the value to a string. **This behavior is deprecated.** Future versions of Node.js may throw an error when the value is not a string, number, or boolean.

```js import env from 'node:process';

env.test = null; console.log(env.test); // => 'null' env.test = undefined; console.log(env.test); // => 'undefined' ```

Use `delete` to delete a property from `process.env`.

```js import env from 'node:process';

env.TEST = 1; delete env.TEST; console.log(env.TEST); // => undefined ```

On Windows operating systems, environment variables are case-insensitive.

```js import env from 'node:process';

env.TEST = 1; console.log(env.test); // => 1 ```

Unless explicitly specified when creating a `Worker` instance, each `Worker` thread has its own copy of `process.env`, based on its parent thread's `process.env`, or whatever was specified as the `env` option to the `Worker` constructor. Changes to `process.env` will not be visible across `Worker` threads, and only the main thread can make changes that are visible to the operating system or to native add-ons. On Windows, a copy of `process.env` on a `Worker` instance operates in a case-sensitive manner unlike the main thread.
env
.
string | undefined
NEXT_PUBLIC_ALCHEMY_POLICY_ID
,
});
const setClient: (value: React.SetStateAction<SmartWalletClient | undefined>) => void
setClient
(
const client: SmartWalletClient<undefined>
client
);
})(); }, [
embeddedWallet: PrivyWallet
embeddedWallet
,
const signAuthorization: any
signAuthorization
]);
return {
client: SmartWalletClient | undefined
client
};
};

When using the SmartWalletClient you must set the eip7702Auth capability to true, as shown in smart-wallet-demo.tsx

import { 
any
ConnectedWallet
as
import PrivyWallet
PrivyWallet
} from "@privy-io/react-auth";
import {
import useSmartEmbeddedWallet
useSmartEmbeddedWallet
} from "../hooks/use-smart-embedded-wallet";
import {
function useCallback<T extends Function>(callback: T, deps: React.DependencyList): T

useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.

useCallback
,
function useState<S>(initialState: S | (() => S)): [S, React.Dispatch<React.SetStateAction<S>>] (+1 overload)

Returns a stateful value, and a function to update it.

useState
} from "react";
import type {
type Address = `0x${string}`
Address
,
type Hex = `0x${string}`
Hex
} from "viem";
export const
const SmartWalletDemo: ({ embeddedWallet, }: { embeddedWallet: PrivyWallet; }) => JSX.Element
SmartWalletDemo
= ({
embeddedWallet: PrivyWallet
embeddedWallet
,
}: {
embeddedWallet: PrivyWallet
embeddedWallet
:
import PrivyWallet
PrivyWallet
;
}) => { const {
const client: any
client
} =
import useSmartEmbeddedWallet
useSmartEmbeddedWallet
({
embeddedWallet: PrivyWallet
embeddedWallet
});
const [
const status: { status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }
status
,
const setStatus: React.Dispatch<React.SetStateAction<{ status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }>>
setStatus
] =
useState<{ status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }>(initialState: { status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; } | (() => { status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; })): [{ status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }, React.Dispatch<React.SetStateAction<{ status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }>>] (+1 overload)

Returns a stateful value, and a function to update it.

useState
<
| {
status: "idle" | "error" | "sending"
status
: "idle" | "error" | "sending" }
| {
status: "success"
status
: "success";
txHash: `0x${string}`
txHash
:
type Hex = `0x${string}`
Hex
}
>({
status: "idle" | "error" | "sending"
status
: "idle" });
const
const delegateAndSend: () => Promise<void>
delegateAndSend
=
useCallback<() => Promise<void>>(callback: () => Promise<void>, deps: React.DependencyList): () => Promise<void>

useCallback will return a memoized version of the callback that only changes if one of the inputs has changed.

useCallback
(async () => {
if (!
const client: any
client
) {
return; }
const setStatus: (value: React.SetStateAction<{ status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }>) => void
setStatus
({
status: "idle" | "error" | "sending"
status
: "sending" });
try { const {
any
preparedCallIds
: [
const callId: any
callId
],
} = await
const client: any
client
.
any
sendCalls
({
capabilities: { eip7702Auth: boolean; }
capabilities
: {
eip7702Auth: boolean
eip7702Auth
: true,
},
from: `0x${string}`
from
:
embeddedWallet: PrivyWallet
embeddedWallet
.
any
address
as
type Address = `0x${string}`
Address
,
calls: { to: string; data: string; }[]
calls
: [
{
to: string
to
: "0x0000000000000000000000000000000000000000",
data: string
data
: "0x",
}, ], }); if (!
const callId: any
callId
) {
throw new
var Error: ErrorConstructor new (message?: string) => Error
Error
("Missing call id");
} const {
const receipts: any
receipts
} = await
const client: any
client
.
any
waitForCallsStatus
({
id: any
id
:
const callId: any
callId
});
if (!
const receipts: any
receipts
?.
any
length
) {
throw new
var Error: ErrorConstructor new (message?: string) => Error
Error
("Missing transaction receipts");
} const [
const receipt: any
receipt
] =
const receipts: any
receipts
;
if (
const receipt: any
receipt
?.
any
status
!== "success") {
throw new
var Error: ErrorConstructor new (message?: string) => Error
Error
("Transaction failed");
}
const setStatus: (value: React.SetStateAction<{ status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }>) => void
setStatus
({
status: "success"
status
: "success",
txHash: `0x${string}`
txHash
:
const receipt: any
receipt
.
any
transactionHash
});
} catch (
function (local var) err: unknown
err
) {
var console: Console

The console module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.

The module exports two specific components:

  • A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream. * A global console instance configured to write to process.stdout and process.stderr. The global console can be used without importing the node:console module.

Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the note on process I/O for more information.

Example using the global console:


const name = 'Will Robinson'; console.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ```

Example using the `Console` class:

```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err);

myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err

const name = 'Will Robinson'; myConsole.warn(`Danger $name! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```
console
.
Console.error(message?: any, ...optionalParams: any[]): void (+1 overload)

Prints to stderr with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to printf(3) (the arguments are all passed to util.format()).

js const code = 5; console.error('error #%d', code); // Prints: error #5, to stderr console.error('error', code); // Prints: error 5, to stderr

If formatting elements (e.g. %d) are not found in the first string then util.inspect() is called on each argument and the resulting string values are concatenated. See util.format() for more information.

error
("Transaction failed:",
function (local var) err: unknown
err
);
const setStatus: (value: React.SetStateAction<{ status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }>) => void
setStatus
({
status: "idle" | "error" | "sending"
status
: "error" });
} }, [
const client: any
client
,
embeddedWallet: PrivyWallet
embeddedWallet
]);
return ( <
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
React.HTMLAttributes<HTMLDivElement>.className?: string | undefined
className
="flex flex-col gap-4">
<
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
React.JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
h2
React.HTMLAttributes<T>.className?: string | undefined
className
="text-lg font-semibold text-gray-900">
Embedded EOA Address </
React.JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
h2
>
<
React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
React.HTMLAttributes<T>.className?: string | undefined
className
="text-gray-600 font-mono break-all">
{
embeddedWallet: PrivyWallet
embeddedWallet
.
any
address
}
</
React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
>
</
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
<
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
React.DOMAttributes<HTMLButtonElement>.onClick?: React.MouseEventHandler<HTMLButtonElement> | undefined
onClick
={
const delegateAndSend: () => Promise<void>
delegateAndSend
}
React.ButtonHTMLAttributes<HTMLButtonElement>.disabled?: boolean | undefined
disabled
={!
const client: any
client
||
const status: { status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }
status
.
status: "idle" | "error" | "sending" | "success"
status
=== "sending"}
React.HTMLAttributes<T>.className?: string | undefined
className
={`w-full py-3 px-4 rounded-lg font-semibold text-white transition-colors ${
const status: { status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }
status
.
status: "idle" | "error" | "sending" | "success"
status
=== "sending"
? "bg-indigo-400 cursor-not-allowed" : "bg-indigo-600 hover:bg-indigo-700" }`} > {
const status: { status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }
status
.
status: "idle" | "error" | "sending" | "success"
status
=== "sending"
? "Sending..." : "Upgrade & Send Sponsored Transaction"} </
React.JSX.IntrinsicElements.button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>
button
>
{
const status: { status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }
status
.
status: "idle" | "error" | "sending" | "success"
status
=== "success" && (
<
React.JSX.IntrinsicElements.section: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
section
React.HTMLAttributes<T>.className?: string | undefined
className
="bg-green-50 rounded-xl shadow-lg p-6 border border-green-200">
<
React.JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
h2
React.HTMLAttributes<T>.className?: string | undefined
className
="text-lg font-semibold text-green-900 mb-4">
Congrats! Sponsored transaction successful! </
React.JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
h2
>
<
React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
React.HTMLAttributes<T>.className?: string | undefined
className
="text-green-700 mb-4">
You've successfully upgraded your EOA to a smart account and sent your first sponsored transaction.{" "} <
React.JSX.IntrinsicElements.a: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>
a
React.AnchorHTMLAttributes<HTMLAnchorElement>.href?: string | undefined
href
="https://www.alchemy.com/docs/wallets/transactions/using-eip-7702"
React.HTMLAttributes<T>.className?: string | undefined
className
="text-indigo-600 hover:underline"
React.AnchorHTMLAttributes<HTMLAnchorElement>.target?: React.HTMLAttributeAnchorTarget | undefined
target
="_blank"
React.HTMLAttributes<HTMLAnchorElement>.rel?: string | undefined
rel
="noopener noreferrer"
> Keep building </
React.JSX.IntrinsicElements.a: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>
a
>
. </
React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
>
<
React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
React.HTMLAttributes<T>.className?: string | undefined
className
="text-green-700">
<
React.JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
strong
>Transaction Hash:</
React.JSX.IntrinsicElements.strong: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
strong
>{" "}
<
React.JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span
React.HTMLAttributes<T>.className?: string | undefined
className
="font-mono break-all">{
const status: { status: "success"; txHash: Hex; }
status
.
txHash: `0x${string}`
txHash
}</
React.JSX.IntrinsicElements.span: React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>
span
>
</
React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
>
</
React.JSX.IntrinsicElements.section: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
section
>
)} {
const status: { status: "idle" | "error" | "sending"; } | { status: "success"; txHash: Hex; }
status
.
status: "idle" | "error" | "sending" | "success"
status
=== "error" && (
<
React.JSX.IntrinsicElements.section: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
section
React.HTMLAttributes<T>.className?: string | undefined
className
="bg-red-50 rounded-xl shadow-lg p-6 border border-red-200">
<
React.JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
h2
React.HTMLAttributes<T>.className?: string | undefined
className
="text-lg font-semibold text-red-900 mb-4">
Transaction Failed </
React.JSX.IntrinsicElements.h2: React.DetailedHTMLProps<React.HTMLAttributes<HTMLHeadingElement>, HTMLHeadingElement>
h2
>
<
React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
React.HTMLAttributes<T>.className?: string | undefined
className
="text-red-700">
There was an error sending your sponsored transaction. Please try again. </
React.JSX.IntrinsicElements.p: React.DetailedHTMLProps<React.HTMLAttributes<HTMLParagraphElement>, HTMLParagraphElement>
p
>
</
React.JSX.IntrinsicElements.section: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>
section
>
)} </
React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div
>
); };

Next steps

Build more: