Unlocking Account Abstraction
Smart accounts for the next billion users
Dror Tirosh
ERC-4337 Core Team
What is Account Abstraction?
What are we abstracting exactly, and why?
What We Abstract ?
The Main Components of ERC-4337
Bundler
EntryPoint Contract
Account Contract
Paymaster
UserOperation
UserOp Mempool
UserOperation Lifecycle
Bundler
User’s Contract Account�
validateUserOp()
execute()
EntryPoint Contract��simulateUserOp()�handleOps()
Paymaster Contract��validatePaymasterUserOp()�postOp()
from:
calldata:
paymaster:
signature:
from:
calldata:
paymaster:
signature:
from:
calldata:
paymaster:
signature:
from:
calldata:
paymaster:
signature:
from:
calldata:
paymaster:
signature:
from:
calldata:
paymaster:
signature:
UserOp mempool
Crypto wallet image by Frühstück from Noun Project
from:
calldata:
paymaster:
signature:
from:
calldata:
paymaster:
signature:
from:
calldata:
paymaster:
signature:
simulateUserOp()
Simulation Phase
(“dry run”: view calls only)
validateUserOp()
validatePaymasterUserOp()
Execution Phase
handleOps()
validateUserOp()
validatePaymasterUserOp()
execute()
postOp()
EOA txs VS UserOperations
Plain old Accounts (EOA)
Account-Abstraction
BaseAccount.sol
function validateUserOp(UserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
external override virtual returns (uint256 validationData) {
_requireFromEntryPoint();
validationData = _validateSignature(userOp, userOpHash);
if (userOp.initCode.length == 0) {
_validateAndUpdateNonce(userOp);
}
_payPrefund(missingAccountFunds);
}
function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash)
internal virtual returns (uint256 validationData);
function _packValidationData(bool sigFailed, uint48 validUntil, uint48 validAfter)
pure returns (uint256);
SimpleAccount: Validation
function _validateAndUpdateNonce(UserOperation calldata userOp) internal override {
require(_nonce++ == userOp.nonce, "account: invalid nonce");
}
function _validateSignature(UserOperation calldata userOp, bytes32 userOpHash)
internal override virtual returns (uint256 validationData) {
bytes32 hash = userOpHash.toEthSignedMessageHash();
if (owner != hash.recover(userOp.signature))
return SIG_VALIDATION_FAILED;
return 0;
}
SimpleAccount: Execution
function execute(address dest, uint256 value, bytes calldata func) external {
_requireFromEntryPointOrOwner();
_call(dest, value, func);
}
function executeBatch(address[] calldata dest, bytes[] calldata func) external {
_requireFromEntryPointOrOwner();
require(dest.length == func.length, "wrong array lengths");
for (uint256 i = 0; i < dest.length; i++) {
_call(dest[i], 0, func[i]);
}
}
BasePaymaster.sol
function validatePaymasterUserOp(UserOperation userOp, bytes32 userOpHash, uint256 maxCost)
external override returns (bytes memory context, uint256 validationData) {
_requireFromEntryPoint();
return _validatePaymasterUserOp(userOp, userOpHash, maxCost);
}
function postOp(PostOpMode mode, bytes calldata context, uint256 actualGasCost) external override {
_requireFromEntryPoint();
_postOp(mode, context, actualGasCost);
}
The Possibilities are Endless
Introducing: Trampoline
Trampoline: account-api
Trampoline: Onboarding flow - add chrome extension
Trampoline: Transaction Flow
Demo Time!
“It takes a village”
Developer platforms already making ERC-4337 a reality
ERC-4337 Developer Platforms
Going to BUIDL? We’re here to help