This document outlines the improvements made to ensure avatar creation is fully gasless through the paymaster contract.
File: lib/services/blockchain_service.dart
Changes:
- Removed redundant whitelisting logic (now handled by
TransactionService) - Removed redundant balance checks (now handled by
TransactionService) - Simplified error handling with better error messages
- All transaction routing now goes through
TransactionService.sendTransaction()
Before: The method had ~100 lines of manual whitelisting and balance checking logic.
After: The method is now ~50 lines and delegates all complexity to TransactionService.
How it works:
- When a user creates their first avatar, they may not have a smart contract account yet
- The
AccountAbstractionServicegeneratesinitCodewhich includes:- Factory contract address
- Function call to
createAccount(owner, salt) - Owner address (the user's EOA)
- Salt for deterministic address generation
- The bundler receives the UserOperation with
initCode - The bundler computes the account address from
initCodeusing CREATE2 - The account is created as part of the UserOperation execution
- The paymaster sponsors the entire transaction, including account creation
When sponsorAllTransactions is enabled:
- User submits UserOperation with
initCode(for account creation) - Bundler computes account address from
initCode - EntryPoint calls paymaster's
validateAndPay(accountAddress, gasCost) - Paymaster checks
sponsorAllTransactionsflag - If enabled, paymaster auto-whitelists the account address and sponsors the transaction
- Account creation is gasless!
When sponsorAvatarCreation is enabled:
- Same flow as above
- Paymaster checks
sponsorAvatarCreationflag - If enabled, paymaster auto-whitelists and sponsors
- Account creation is gasless!
Better error messages:
- If paymaster is enabled but transaction fails: Clear message about paymaster funding
- If bundler is not configured: Clear message with setup instructions
- If insufficient funds: Context-aware message based on paymaster status
Issue: The current validateAndPay function receives only the account address, not the owner address. For account creation, the paymaster needs to check whitelisting based on the owner address (EOA), not the account address.
Current Workaround:
- When
sponsorAllTransactionsorsponsorAvatarCreationis enabled, the paymaster auto-whitelists the account address - Since the account address is computed deterministically from the owner address via CREATE2, this works for account creation
- However, it means the paymaster whitelists based on account address, not owner address
Future Improvement:
- Implement full ERC-4337
validatePaymasterUserOpfunction - This function receives the full UserOperation, including
initCode - Extract owner address from
initCode - Check whitelisting based on owner address
- This allows more granular control (e.g., whitelist owner once, all their accounts are sponsored)
- Create avatar with new wallet (no smart account) - should be gasless
- Create avatar with existing smart account - should be gasless
- Verify paymaster balance decreases after transaction
- Verify account is created on-chain
- Verify avatar is created on-chain
- Test with
sponsorAllTransactionsenabled - Test with
sponsorAvatarCreationenabled - Test error handling when paymaster has insufficient funds
- Test error handling when bundler is not configured
-
Paymaster Contract:
- Deploy
GoldfirePaymastercontract - Fund it with MATIC
- Enable
sponsorAllTransactionsorsponsorAvatarCreation
- Deploy
-
Bundler:
- Set up ERC-4337 bundler (e.g., Alchemy, Stackup)
- Configure
bundlerRpcUrlinapp_constants.dart
-
EntryPoint:
- Use standard ERC-4337 EntryPoint contract
- Configure in
app_constants.dart
User creates avatar
↓
AvatarNotifier.createPrimaryAvatar()
↓
BlockchainService.createAvatarProfile()
↓
TransactionService.sendTransaction()
↓
TransactionService._ensureUserWhitelisted() // Whitelists user if needed
↓
TransactionService._sendViaAccountAbstraction()
↓
AccountAbstractionService.createUserOperation() // Generates initCode if needed
↓
AccountAbstractionService.sendUserOperation() // Sends to bundler
↓
Bundler processes UserOperation
↓
EntryPoint.executeUserOp()
↓
Paymaster.validateAndPay() // Sponsors transaction
↓
Account created (if initCode present)
↓
Avatar created
↓
Transaction complete (gasless!)
- Test the improvements with a real wallet and paymaster
- Monitor paymaster balance and refill as needed
- Consider implementing full ERC-4337 paymaster interface for better owner-based whitelisting
- Add monitoring for failed transactions to identify issues early