Sign EIP-7702 authorizations to enable your embedded wallets to gain account abstraction capabilities such as transaction bundling, gas sponsorship, and custom permissions.
EIP-7702 enables externally owned accounts (EOAs) to delegate their execution to smart contract code, allowing EOA wallets to function like smart contract wallets.
Overview
EIP-7702 is a groundbreaking proposal that allows EOA wallets to temporarily delegate their code execution to a smart contract. This enables powerful account abstraction features without requiring users to migrate to new wallet addresses.
Key Benefits
Transaction batching : Execute multiple operations in a single transaction
Gas sponsorship : Enable gasless transactions for users
Custom permissions : Implement spending limits and approval logic
Session keys : Allow temporary key access with limited permissions
Social recovery : Add recovery mechanisms to EOA wallets
Backwards compatibility : EOAs can revert to normal operation anytime
How It Works
User signs an EIP-7702 authorization delegating to a contract
Authorization is included in a transaction
During that transaction, the EOA behaves like the delegated contract
After the transaction, the EOA returns to normal
Authorization can be revoked or updated at any time
React SDK
Basic Usage
With UI Customization
To sign an EIP-7702 authorization, use the sign7702Authorization method from the useSign7702Authorization hook: import { useMoonKey } from '@moon-key/react-auth' ;
import { useSign7702Authorization } from '@moon-key/react-auth/ethereum' ;
function Sign7702Button () {
const { user } = useMoonKey ();
const { sign7702Authorization } = useSign7702Authorization ();
const handleSign = async () => {
if ( ! user ?. wallet ) {
alert ( 'No wallet found' );
return ;
}
try {
const authorization = await sign7702Authorization ({
contractAddress: '0x1234567890abcdef1234567890abcdef12345678' ,
chainId: 1 , // Ethereum mainnet
nonce: 0 // Optional
});
console . log ( 'Authorization signed:' , authorization );
alert ( 'Successfully signed EIP-7702 authorization!' );
// Use authorization with your AA provider
} catch ( error ) {
console . error ( 'Failed to sign authorization:' , error );
}
};
return (
< button onClick = { handleSign } >
Enable Account Abstraction
</ button >
);
}
Parameters
The address of the smart contract whose code the EOA will delegate to. This is typically an account implementation contract from your AA provider. Example: contractAddress : '0x1234567890abcdef1234567890abcdef12345678'
The chain ID where the authorization will be used. Example: chainId : 1 // Ethereum mainnet
The nonce for the authorization. If not provided, the current transaction count for the wallet will be used automatically. Example:
Options Object
The second parameter is an optional configuration object:
Configuration for the authorization confirmation modal. Custom title for the confirmation modal.
Description text shown in the modal.
Text for the confirm button.
Text for the cancel button.
Whether to show the confirmation modal. Set to false to sign without confirmation.
Specific wallet to use for signing. If not provided, uses the user’s default wallet.
Returns
The signed EIP-7702 authorization object. Show authorization properties
The smart contract address the EOA is delegating to.
The chain ID for the authorization.
The nonce used in the authorization.
The r value of the ECDSA signature.
The s value of the ECDSA signature.
The yParity value of the ECDSA signature (0 or 1).
Complete Examples
'use client' ;
import { useMoonKey } from '@moon-key/react-auth' ;
import { useSign7702Authorization } from '@moon-key/react-auth/ethereum' ;
import { useState } from 'react' ;
export default function GasSponsorshipSetup () {
const { user , isAuthenticated } = useMoonKey ();
const { sign7702Authorization } = useSign7702Authorization ();
const [ isEnabled , setIsEnabled ] = useState ( false );
const [ isEnabling , setIsEnabling ] = useState ( false );
const handleEnable = async () => {
if ( ! user ?. wallet ) {
alert ( 'Please connect your wallet first' );
return ;
}
setIsEnabling ( true );
try {
// Your AA provider's account implementation contract
const accountImplementation = '0xGasSponsorshipImplementation...' ;
// Sign the EIP-7702 authorization
const authorization = await sign7702Authorization ({
contractAddress: accountImplementation ,
chainId: 8453 // Base
}, {
uiConfig: {
title: 'Enable Gasless Transactions' ,
description: 'Sign to enable gas sponsorship for your transactions' ,
confirmButtonText: 'Enable Gasless'
}
});
console . log ( 'Authorization created:' , authorization );
// Register with your gas sponsorship service
const response = await fetch ( '/api/gas-sponsorship/register' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({
walletAddress: user . wallet . address ,
authorization ,
chainId: 8453
})
});
if ( response . ok ) {
setIsEnabled ( true );
alert ( 'Gasless transactions enabled!' );
} else {
throw new Error ( 'Failed to register for gas sponsorship' );
}
} catch ( error ) {
console . error ( 'Failed to enable gas sponsorship:' , error );
alert ( 'Failed to enable gasless transactions' );
} finally {
setIsEnabling ( false );
}
};
if ( ! isAuthenticated ) {
return < p > Please sign in to enable gas sponsorship </ p > ;
}
return (
< div className = "gas-sponsorship" >
< h2 > Gasless Transactions </ h2 >
< p > Enable gas sponsorship to send transactions without paying gas fees </ p >
{ ! isEnabled ? (
< button
onClick = { handleEnable }
disabled = { isEnabling }
className = "enable-button"
>
{ isEnabling ? 'Enabling...' : 'Enable Gas Sponsorship' }
</ button >
) : (
< div className = "enabled-status" >
< p > ✅ Gas sponsorship is enabled </ p >
< p > You can now send transactions without gas fees! </ p >
</ div >
) }
</ div >
);
}
UI Customization
Customize the authorization signing modal:
await sign7702Authorization ({
contractAddress: '0x...' ,
chainId: 1
}, {
uiConfig: {
title: 'Enable Smart Features' ,
description: 'This allows your wallet to use advanced account abstraction features' ,
confirmButtonText: 'Enable' ,
cancelButtonText: 'Cancel'
}
});
Hide Modal
await sign7702Authorization ({
contractAddress: '0x...' ,
chainId: 1
}, {
uiConfig: {
showWalletUI: false
}
});
Hiding the modal removes the user’s ability to review the authorization before signing. Only use this for trusted operations or after obtaining explicit user consent.
Best Practices
Validate contract implementation
Always verify the contract address before delegating: // ✅ Good - Validate contract
const isValidImplementation = await verifyContract ( contractAddress );
if ( ! isValidImplementation ) {
throw new Error ( 'Invalid or untrusted contract' );
}
await sign7702Authorization ({
contractAddress ,
chainId: 1
});
Use appropriate chain IDs
Ensure the chain ID matches your target network: const CHAIN_IDS = {
mainnet: 1 ,
base: 8453 ,
optimism: 10 ,
arbitrum: 42161
};
// Use correct chain ID
await sign7702Authorization ({
contractAddress: accountImplementation ,
chainId: CHAIN_IDS . base
});
Let MoonKey handle nonces automatically unless you have specific requirements: // ✅ Good - Let MoonKey handle nonce
await sign7702Authorization ({
contractAddress ,
chainId: 1
// nonce is automatically determined
});
// Only specify nonce if you need precise control
await sign7702Authorization ({
contractAddress ,
chainId: 1 ,
nonce: specificNonce
});
Educate users about delegation
Clearly explain what authorization means: const handleEnable = async () => {
const confirmed = confirm (
'This will allow your wallet to use smart contract features. ' +
'You maintain full control and can revoke this at any time. Continue?'
);
if ( ! confirmed ) return ;
await sign7702Authorization ({
contractAddress ,
chainId: 1
});
};
Provide revocation mechanism
Allow users to revoke authorization: async function revokeAuthorization ( walletAddress : string ) {
// Call your AA provider's revocation endpoint
await fetch ( '/api/aa-provider/revoke' , {
method: 'POST' ,
body: JSON . stringify ({ walletAddress })
});
alert ( 'Authorization revoked. Your wallet is back to normal.' );
}
Provide clear error messages: try {
await sign7702Authorization ({
contractAddress ,
chainId: 1
});
} catch ( error : any ) {
if ( error . code === 'USER_REJECTED' ) {
console . log ( 'User cancelled authorization' );
return ;
} else if ( error . message ?. includes ( 'nonce' )) {
alert ( 'Nonce error. Please try again.' );
} else if ( error . message ?. includes ( 'chain' )) {
alert ( 'Wrong network. Please switch to the correct network.' );
} else {
alert ( 'Failed to authorize: ' + error . message );
}
}
Troubleshooting
Authorization not working
Common issues:
Wrong contract address : Verify the account implementation contract
Chain ID mismatch : Ensure authorization chain matches transaction chain
Nonce issues : Let MoonKey handle nonce automatically
Contract not compatible : Ensure contract supports EIP-7702
// Debug authorization
const authorization = await sign7702Authorization ({
contractAddress ,
chainId
});
console . log ( 'Authorization details:' , {
contract: authorization . contractAddress ,
chain: authorization . chainId ,
nonce: authorization . nonce
});
User cancelled authorization
Handle cancellations gracefully: try {
const authorization = await sign7702Authorization ({
contractAddress ,
chainId: 1
});
} catch ( error : any ) {
if ( error . code === 'USER_REJECTED' ) {
// User deliberately cancelled - don't show error
console . log ( 'Authorization cancelled by user' );
return ;
}
// Handle other errors
alert ( 'Authorization failed: ' + error . message );
}
Ensure user is on the correct network: import { useWalletChain } from '@moon-key/react-auth/ethereum' ;
const { chainId , switchChain } = useWalletChain ();
const targetChainId = 8453 ; // Base
if ( chainId !== targetChainId ) {
try {
await switchChain ( targetChainId );
} catch ( error ) {
alert ( 'Please switch to Base network' );
return ;
}
}
await sign7702Authorization ({
contractAddress ,
chainId: targetChainId
});
Validate contract addresses: import { isAddress } from 'viem' ;
if ( ! isAddress ( contractAddress )) {
alert ( 'Invalid contract address' );
return ;
}
// Optionally verify contract is deployed
const code = await publicClient . getBytecode ({
address: contractAddress as `0x ${ string } `
});
if ( ! code || code === '0x' ) {
alert ( 'Contract not found at this address' );
return ;
}
await sign7702Authorization ({
contractAddress ,
chainId: 1
});
Next Steps