-
Notifications
You must be signed in to change notification settings - Fork 11.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Start initial work on ERC4337 interface and helpers
- Loading branch information
Showing
2 changed files
with
156 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
import {PackedUserOperation} from "../interfaces/IERC4337.sol"; | ||
import {Math} from "../utils/math/Math.sol"; | ||
|
||
// TODO: move that to a dedicated file in `contracts/utils/math` ? | ||
library Unpack { | ||
function split(bytes32 packed) internal pure returns (uint256 high128, uint256 low128) { | ||
return (uint128(bytes16(packed)), uint128(uint256(packed))); | ||
} | ||
|
||
function high(bytes32 packed) internal pure returns (uint256) { | ||
return uint256(packed) >> 128; | ||
} | ||
|
||
function low(bytes32 packed) internal pure returns (uint256) { | ||
return uint128(uint256(packed)); | ||
} | ||
} | ||
|
||
library UserOperationUtils { | ||
using Unpack for bytes32; | ||
|
||
uint256 public constant PAYMASTER_VALIDATION_GAS_OFFSET = 20; | ||
uint256 public constant PAYMASTER_POSTOP_GAS_OFFSET = 36; | ||
uint256 public constant PAYMASTER_DATA_OFFSET = 52; | ||
|
||
// Need to fuzz this against `userOp.sender` | ||
function getSender(PackedUserOperation calldata userOp) internal pure returns (address) { | ||
address data; | ||
assembly { | ||
data := calldataload(userOp) | ||
} | ||
return address(uint160(data)); | ||
} | ||
|
||
function getMaxPriorityFeePerGas(PackedUserOperation calldata userOp) internal pure returns (uint256) { | ||
return userOp.gasFees.high(); | ||
} | ||
|
||
function getMaxFeePerGas(PackedUserOperation calldata userOp) internal pure returns (uint256) { | ||
return userOp.gasFees.low(); | ||
} | ||
|
||
function getGasPrice(PackedUserOperation calldata userOp) internal view returns (uint256) { | ||
unchecked { | ||
(uint256 maxPriorityFeePerGas, uint256 maxFeePerGas) = userOp.gasFees.split(); | ||
return | ||
maxFeePerGas == maxPriorityFeePerGas | ||
? maxFeePerGas | ||
: Math.min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); | ||
} | ||
} | ||
|
||
function getVerificationGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { | ||
return userOp.accountGasLimits.high(); | ||
} | ||
|
||
function getCallGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { | ||
return userOp.accountGasLimits.low(); | ||
} | ||
|
||
function getPaymasterVerificationGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { | ||
return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET:PAYMASTER_POSTOP_GAS_OFFSET])); | ||
} | ||
|
||
function getPostOpGasLimit(PackedUserOperation calldata userOp) internal pure returns (uint256) { | ||
return uint128(bytes16(userOp.paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET:PAYMASTER_DATA_OFFSET])); | ||
} | ||
|
||
function getPaymasterStaticFields( | ||
bytes calldata paymasterAndData | ||
) internal pure returns (address paymaster, uint256 validationGasLimit, uint256 postOpGasLimit) { | ||
return ( | ||
address(bytes20(paymasterAndData[:PAYMASTER_VALIDATION_GAS_OFFSET])), | ||
uint128(bytes16(paymasterAndData[PAYMASTER_VALIDATION_GAS_OFFSET:PAYMASTER_POSTOP_GAS_OFFSET])), | ||
uint128(bytes16(paymasterAndData[PAYMASTER_POSTOP_GAS_OFFSET:PAYMASTER_DATA_OFFSET])) | ||
); | ||
} | ||
|
||
function encode(PackedUserOperation calldata userOp) internal pure returns (bytes memory ret) { | ||
return | ||
abi.encode( | ||
userOp.sender, | ||
userOp.nonce, | ||
calldataKeccak(userOp.initCode), | ||
calldataKeccak(userOp.callData), | ||
userOp.accountGasLimits, | ||
userOp.preVerificationGas, | ||
userOp.gasFees, | ||
calldataKeccak(userOp.paymasterAndData) | ||
); | ||
} | ||
|
||
function hash(PackedUserOperation calldata userOp) internal pure returns (bytes32) { | ||
return keccak256(encode(userOp)); | ||
} | ||
|
||
function calldataKeccak(bytes calldata data) private pure returns (bytes32 ret) { | ||
assembly ("memory-safe") { | ||
let ptr := mload(0x40) | ||
let len := data.length | ||
calldatacopy(ptr, data.offset, len) | ||
ret := keccak256(ptr, len) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
pragma solidity ^0.8.20; | ||
|
||
/* | ||
struct UserOperation { | ||
address sender; // The account making the operation | ||
uint256 nonce; // Anti-replay parameter (see “Semi-abstracted Nonce Support” ) | ||
address factory; // account factory, only for new accounts | ||
bytes factoryData; // data for account factory (only if account factory exists) | ||
bytes callData; // The data to pass to the sender during the main execution call | ||
uint256 callGasLimit; // The amount of gas to allocate the main execution call | ||
uint256 verificationGasLimit; // The amount of gas to allocate for the verification step | ||
uint256 preVerificationGas; // Extra gas to pay the bunder | ||
uint256 maxFeePerGas; // Maximum fee per gas (similar to EIP-1559 max_fee_per_gas) | ||
uint256 maxPriorityFeePerGas; // Maximum priority fee per gas (similar to EIP-1559 max_priority_fee_per_gas) | ||
address paymaster; // Address of paymaster contract, (or empty, if account pays for itself) | ||
uint256 paymasterVerificationGasLimit; // The amount of gas to allocate for the paymaster validation code | ||
uint256 paymasterPostOpGasLimit; // The amount of gas to allocate for the paymaster post-operation code | ||
bytes paymasterData; // Data for paymaster (only if paymaster exists) | ||
bytes signature; // Data passed into the account to verify authorization | ||
} | ||
*/ | ||
|
||
struct PackedUserOperation { | ||
address sender; | ||
uint256 nonce; | ||
bytes initCode; // concatenation of factory address and factoryData (or empty) | ||
bytes callData; | ||
bytes32 accountGasLimits; // concatenation of verificationGas (16 bytes) and callGas (16 bytes) | ||
uint256 preVerificationGas; | ||
bytes32 gasFees; // concatenation of maxPriorityFee (16 bytes) and maxFeePerGas (16 bytes) | ||
bytes paymasterAndData; // concatenation of paymaster fields (or empty) | ||
bytes signature; | ||
} | ||
|
||
interface IAccount { | ||
function validateUserOp( | ||
PackedUserOperation calldata userOp, | ||
bytes32 userOpHash, | ||
uint256 missingAccountFunds | ||
) external returns (uint256 validationData); | ||
} | ||
|
||
interface IAccountExecute { | ||
function executeUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash) external; | ||
} |