Skip to content

Integrate with EIP-7702

EIP-7702 is an Ethereum Improvement Proposal that allows Externally Owned Accounts (EOAs) to temporarily behave like smart contract accounts. This enables regular wallets to access smart account features like:

  • Gasless transactions
  • Batch operations
  • Custom transaction logic
  • Enhanced security features

Example embedded wallets:

With Openfort Embedded Wallets

This sample demonstrates how to use Pimlico, a bundler and paymaster service for ERC-4337 accounts, together with Openfort embedded wallets to enable your users to send gasless (sponsored) transactions using EIP-7702 authorization.

Using Openfort to control what delegations are active per wallet and send sponsored transactions.

Project structure

7702/
├── src/
│   ├── app/
│   │   ├── api/
│   │   │   └── protected-create-encryption-session/    # Shield proxy endpoint
│   │   │       └── route.ts
│   │   ├── favicon.ico
│   │   ├── globals.css            # Global styles
│   │   ├── layout.tsx             # Root layout
│   │   └── page.tsx               # Main application page
│   ├── components/
│   │   ├── Providers.tsx          # Context providers
│   │   ├── ui/                    # UI components
│   │   │   ├── button.tsx
│   │   │   └── card.tsx
│   │   └── UserOperation.tsx      # User operation component
│   └── lib/
│       ├── utils.ts               # Utility functions
│       └── wagmiConfig.ts         # Wagmi configuration
├── public/                        # Static assets
├── .env.example                   # Environment template
├── package.json                   # Project dependencies
├── next.config.js                 # Next.js configuration
└── README.md                      # Project documentation

Next.js application that integrates Openfort embedded wallets with Permissionless SDK and Pimlico to enable EIP-7702 authorization and gasless transaction execution on Ethereum Sepolia testnet.

Key features

  • Non-custodial embedded wallets via Openfort embedded wallets
  • EIP-7702 authorization signing
  • Gasless transaction execution via Pimlico paymaster
  • Simple smart account creation with Permissionless SDK
  • Environment configuration validation
ComponentTechnology
FrontendNext.js + React
BlockchainEthereum Sepolia
Key Libraries@openfort/react, permissionless, viem, wagmi
PaymasterPimlico
StylingTailwind CSS

Quick start

Clone the repository using gitpick:

pnpx gitpick openfort-xyz/recipes-hub/tree/main/7702 openfort-7702
cd openfort-7702
pnpm i

Integration details

  • Pimlico Setup
  1. Sign up for a Pimlico account
  2. Generate an API key
  3. Create a sponsorship policy for Ethereum Sepolia
  4. Configure the policy ID in your environment variables
  • Openfort configuration
  1. Create an Openfort project in the dashboard
  2. Set up Shield for embedded wallets
  3. Configure authentication providers

Environment configuration

Create a .env file with the following variables:

# Openfort Configuration
NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY=your_openfort_publishable_key
NEXT_PUBLIC_SHIELD_PUBLISHABLE_KEY=your_shield_publishable_key
NEXT_PUBLIC_OPENFORT_POLICY_ID=your_policy_id
NEXT_PUBLIC_CREATE_ENCRYPTED_SESSION_ENDPOINT=/api/protected-create-encryption-session
 
# Pimlico Configuration
NEXT_PUBLIC_PIMLICO_API_KEY=your_pimlico_api_key
NEXT_PUBLIC_SPONSORSHIP_POLICY_ID=your_sponsorship_policy_id
 
# Network Configuration
NEXT_PUBLIC_SEPOLIA_RPC_URL=your_sepolia_rpc_url

Installation

pnpm install

Development

pnpm dev

Open http://localhost:3000 to view the application.

How it works

Embedded wallet setup

The application creates an embedded wallet for users through Openfort:

<OpenfortProvider
  publishableKey={process.env.NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY!}
  walletConfig={{
    shieldPublishableKey: process.env.NEXT_PUBLIC_SHIELD_PUBLISHABLE_KEY!,
    ethereumProviderPolicyId: process.env.NEXT_PUBLIC_OPENFORT_POLICY_ID,
    createEncryptedSessionEndpoint: process.env.NEXT_PUBLIC_CREATE_ENCRYPTED_SESSION_ENDPOINT,
    accountType: AccountTypeEnum.EOA,
  }}
  uiConfig={{
    authProviders: [
      AuthProvider.EMAIL,
      AuthProvider.GUEST,
      AuthProvider.GOOGLE,
    ],
  }}
>
  {children}
</OpenfortProvider>

Smart account creation

Using Permissionless SDK, create a simple smart account:

const simpleSmartAccount = await toSimpleSmartAccount({
  owner: walletClient,
  entryPoint: {
    address: entryPoint08Address,
    version: "0.8"
  },
  client: publicClient,
  address: walletClient.account.address
})

EIP-7702 authorization

Sign the EIP-7702 authorization using Openfort's hook:

const { signAuthorization } = use7702Authorization()
 
const authorization = await signAuthorization({
  contractAddress: "0xe6Cae83BdE06E4c305530e199D7217f42808555B",
  chainId: sepolia.id,
  nonce: await publicClient.getTransactionCount({
    address: walletClient.account.address
  })
})

Gasless transaction execution

Send sponsored transactions through Pimlico:

const txnHash = await smartAccountClient.sendTransaction({
  calls: [{
    to: zeroAddress,
    data: "0x",
    value: BigInt(0)
  }],
  factory: '0x7702',
  factoryData: '0x',
  paymasterContext: {
    sponsorshipPolicyId: process.env.NEXT_PUBLIC_SPONSORSHIP_POLICY_ID
  },
  authorization
})

With Third Party Wallets

This sample demonstrates how to upgrade your embedded wallets to smart accounts using EIP-7702 authorization signing with third-party embedded wallets like Privy or Turnkey.

Key features

  • Account management through Openfort Dashboard.
  • Sponsor and send transactions through smart accounts with Openfort API.
  • Fast smart account support and implementation.

Integration details

Quick start

Create your delegated account

console.log('=== Step 1: Create User ===');
const responseUser = await fetch(`${url}/v1/players`, {
  method: 'POST',
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "name": "testing v3"
  }),
});
 
if (!responseUser.ok) {
  throw new Error(`Failed to create player: ${responseUser.statusText}`);
}
const responseUserData = await responseUser.json();
console.log('Created player:', responseUserData.id);
 
console.log('\n=== Step 2: Generate Account ===');
// Make sure to import:
// import { generatePrivateKey } from 'viem/accounts';
const privateKey = generatePrivateKey();
console.log('Generated account key:', privateKey);
// Make sure to import:
// import { privateKeyToAccount } from 'viem/accounts';
const account = privateKeyToAccount(privateKey);
console.log('Generated account address:', account.address);
 
console.log('\n=== Step 3: Create Openfort Account (Delegated Account with Calibur) ===');
const responseAccount = await fetch(`${url}/v2/accounts`, {
  method: 'POST',
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "chainType": "EVM",
    "user": responseUserData.id,
    "address": account.address,
    "accountType": "Delegated Account",
    "chainId": chainId,
    "implementationType": implementationType,
  }),
});
if (!responseAccount.ok) {
  throw new Error(`Failed to create account: ${responseAccount.statusText}`);
}
const responseAccountData = await responseAccount.json();
console.log('Created account:', responseAccountData);

Sign EIP-7702 Authorization

console.log('\n=== Step 4: Create Wallet Client ===');
// Make sure to import:
// import { createWalletClient, http } from 'viem';
// import { baseSepolia } from 'viem/chains';
const client = createWalletClient({
  chain: baseSepolia,
  transport: http(),
  account: account,
});
console.log('Created wallet client for Base Sepolia');
 
console.log('\n=== Step 5: Prepare Authorization ===');
const authorization = await client.prepareAuthorization({
  account: account.address,
  contractAddress: responseAccountData.smartAccount.implementationAddress,
});
console.log('Prepared authorization:', authorization);
 
console.log('\n=== Step 6: Sign Authorization ===');
// Make sure to import:
// import { signAuthorization } from 'viem/accounts';
const signedAuthorization = await signAuthorization({
  ...authorization,
  privateKey,
});
console.log('Signed authorization object:', signedAuthorization);
 
console.log('\n=== Step 7: Serialize Authorization ===');
// Make sure to import:
// import { serializeSignature } from 'viem';
const serializedAuthorization = serializeSignature(signedAuthorization);
console.log('Serialized authorization:', serializedAuthorization);

Create gasless transaction

const policy = "pol_..."; // your gas sponsorship policy ID
const contract = "con_"; // your contract ID
 
const responseTransaction = await fetch(`${url}/v1/transaction_intents`, {
  method: 'POST',
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "signedAuthorization": serializedAuthorization,
    "account": responseAccountData.id,
    "chainId": chainId,
    "policy": policy,
    "interactions": [
      {
        "contract": contract,
        "functionName": "mint",
        "functionArgs": ["0x64452Dff1180b21dc50033e1680bB64CDd492582"]
      }
    ]
  }),
});
 
if (!responseTransaction.ok) {
  throw new Error(`Failed to create transaction: ${responseTransaction.statusText}`);
}
const responseTransactionData = await responseTransaction.json();
 
if (!responseTransactionData.nextAction || !responseTransactionData.nextAction.payload) {
  throw new Error(`Invalid response data: ${JSON.stringify(responseTransactionData, null, 2)}`);
}
console.log('Created transaction intent:', responseTransactionData.id);
console.log('Signable hash:', responseTransactionData.nextAction.payload.signableHash);

Sign transaction and submit

console.log('\n=== Step 9: Sign Transaction ===');
const signature = await account.sign({ hash: responseTransactionData.nextAction.payload.signableHash });
console.log('Generated signature:', signature);
 
console.log('\n=== Step 10: Submit Signature ===');
const responseReceipt = await fetch(`${url}/v1/transaction_intents/${responseTransactionData.id}/signature`, {
  method: 'POST',
  headers: {
    "Authorization": `Bearer ${apiKey}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "signature": signature,
  }),
});
 
if (!responseReceipt.ok) {
  throw new Error(`Failed to submit signature: ${responseReceipt.statusText}`);
}
const responseData = await responseReceipt.json();
console.log('Transaction submitted successfully!');
console.log('Response:', responseData);