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 documentationNext.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
| Component | Technology |
|---|---|
| Frontend | Next.js + React |
| Blockchain | Ethereum Sepolia |
| Key Libraries | @openfort/react, permissionless, viem, wagmi |
| Paymaster | Pimlico |
| Styling | Tailwind CSS |
Quick start
Clone the repository using gitpick:
pnpx gitpick openfort-xyz/recipes-hub/tree/main/7702 openfort-7702
cd openfort-7702
pnpm iIntegration details
- Pimlico Setup
- Sign up for a Pimlico account
- Generate an API key
- Create a sponsorship policy for Ethereum Sepolia
- Configure the policy ID in your environment variables
- Openfort configuration
- Create an Openfort project in the dashboard
- Set up Shield for embedded wallets
- 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_urlInstallation
pnpm installDevelopment
pnpm devOpen 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
- Create an Openfort project in the dashboard
- Create a gas sponsorship policy. Take note of the policy ID.
- Create a contract (Sample NFT you can use: 0x280725940d6444374ffcF50fa7D7bb9Eec8d0811). Take note of the contract ID.
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);