Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
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.

GitHub Icon
View Sample Code
GitHub Repository
Complete source code for the Integrate with EIP-7702 integration with Openfort, Pimlico, and gas sponsorship.

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/
│   │   ├── ui/                    # UI components (button, card, etc.)
│   │   └── UserOperation.tsx      # User operation component
│   ├── hooks/                     # Custom React hooks
│   └── 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 Project Keys (from dashboard: Project Settings > API Keys)
NEXT_PUBLIC_OPENFORT_PUBLISHABLE_KEY=your_openfort_publishable_key
OPENFORT_SECRET_KEY=your_openfort_secret_key
NEXT_PUBLIC_OPENFORT_POLICY_ID=your_openfort_policy_id
 
# Openfort Shield Keys (from dashboard: Shield > API Keys)
NEXT_PUBLIC_OPENFORT_SHIELD_PUBLISHABLE_KEY=your_shield_publishable_key
 
# If you want to use AUTOMATIC embedded wallet recovery, an encryption session is required.
# See: https://www.openfort.io/docs/products/embedded-wallet/react-native/quickstart/automatic
# For backend setup, check: https://github.com/openfort-xyz/openfort-backend-quickstart
NEXT_PUBLIC_CREATE_ENCRYPTED_SESSION_ENDPOINT=http://localhost:YOUR_PORT/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_OPENFORT_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 user: ${responseUser.statusText}`);
}
const responseUserData = await responseUser.json();
console.log('Created user:', 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);
Copyright © 2023-present Alamas Labs, Inc