diff --git a/contracts/core/BaseAccount.sol b/contracts/core/BaseAccount.sol index a90d3a06..5c89e0f4 100644 --- a/contracts/core/BaseAccount.sol +++ b/contracts/core/BaseAccount.sol @@ -7,6 +7,7 @@ pragma solidity ^0.8.12; import "../interfaces/IAccount.sol"; import "../interfaces/IEntryPoint.sol"; import "./Helpers.sol"; +import "./UserOperationLib.sol"; /** * Basic account implementation. diff --git a/contracts/core/EntryPoint.sol b/contracts/core/EntryPoint.sol index 8b5fd764..be9eef89 100644 --- a/contracts/core/EntryPoint.sol +++ b/contracts/core/EntryPoint.sol @@ -13,6 +13,7 @@ import "./StakeManager.sol"; import "./SenderCreator.sol"; import "./Helpers.sol"; import "./NonceManager.sol"; +import "./UserOperationLib.sol"; // we also require '@gnosis.pm/safe-contracts' and both libraries have 'IERC165.sol', leading to conflicts import "@openzeppelin/contracts/utils/introspection/ERC165.sol" as OpenZeppelin; @@ -598,11 +599,11 @@ contract EntryPoint is IEntryPoint, StakeManager, NonceManager, ReentrancyGuard, outOpInfo, requiredPreFund ); - + if (!_validateAndUpdateNonce(mUserOp.sender, mUserOp.nonce)) { revert FailedOp(opIndex, "AA25 invalid account nonce"); } - + // A "marker" where account opcode validation is done and paymaster opcode validation // is about to start (used only by off-chain simulateValidation). numberMarker(); diff --git a/contracts/core/UserOperationLib.sol b/contracts/core/UserOperationLib.sol new file mode 100644 index 00000000..376f6761 --- /dev/null +++ b/contracts/core/UserOperationLib.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.12; + +import "../interfaces/UserOperation.sol"; +import {calldataKeccak} from "./Helpers.sol"; + +/** + * Utility functions helpful when working with UserOperation structs. + */ +library UserOperationLib { + /** + * Get sender from user operation data. + * @param userOp - The user operation data. + */ + function getSender( + UserOperation calldata userOp + ) internal pure returns (address) { + address data; + //read sender from userOp, which is first userOp member (saves 800 gas...) + assembly { + data := calldataload(userOp) + } + return address(uint160(data)); + } + + /** + * Relayer/block builder might submit the TX with higher priorityFee, + * but the user should not pay above what he signed for. + * @param userOp - The user operation data. + */ + function gasPrice( + UserOperation calldata userOp + ) internal view returns (uint256) { + unchecked { + uint256 maxFeePerGas = userOp.maxFeePerGas; + uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; + if (maxFeePerGas == maxPriorityFeePerGas) { + //legacy mode (for networks that don't support basefee opcode) + return maxFeePerGas; + } + return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); + } + } + + /** + * Pack the user operation data into bytes for hashing. + * @param userOp - The user operation data. + */ + function pack( + UserOperation calldata userOp + ) internal pure returns (bytes memory ret) { + address sender = getSender(userOp); + uint256 nonce = userOp.nonce; + bytes32 hashInitCode = calldataKeccak(userOp.initCode); + bytes32 hashCallData = calldataKeccak(userOp.callData); + uint256 callGasLimit = userOp.callGasLimit; + uint256 verificationGasLimit = userOp.verificationGasLimit; + uint256 preVerificationGas = userOp.preVerificationGas; + uint256 maxFeePerGas = userOp.maxFeePerGas; + uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; + bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData); + + return abi.encode( + sender, nonce, + hashInitCode, hashCallData, + callGasLimit, verificationGasLimit, preVerificationGas, + maxFeePerGas, maxPriorityFeePerGas, + hashPaymasterAndData + ); + } + + /** + * Hash the user operation data. + * @param userOp - The user operation data. + */ + function hash( + UserOperation calldata userOp + ) internal pure returns (bytes32) { + return keccak256(pack(userOp)); + } + + /** + * The minimum of two numbers. + * @param a - First number. + * @param b - Second number. + */ + function min(uint256 a, uint256 b) internal pure returns (uint256) { + return a < b ? a : b; + } +} diff --git a/contracts/interfaces/UserOperation.sol b/contracts/interfaces/UserOperation.sol index 512798b2..65cd7c3e 100644 --- a/contracts/interfaces/UserOperation.sol +++ b/contracts/interfaces/UserOperation.sol @@ -3,8 +3,6 @@ pragma solidity ^0.8.12; /* solhint-disable no-inline-assembly */ -import {calldataKeccak} from "../core/Helpers.sol"; - /** * User Operation struct * @param sender - The sender account of this request. @@ -34,88 +32,3 @@ struct UserOperation { bytes paymasterAndData; bytes signature; } - -/** - * Utility functions helpful when working with UserOperation structs. - */ -library UserOperationLib { - /** - * Get sender from user operation data. - * @param userOp - The user operation data. - */ - function getSender( - UserOperation calldata userOp - ) internal pure returns (address) { - address data; - //read sender from userOp, which is first userOp member (saves 800 gas...) - assembly { - data := calldataload(userOp) - } - return address(uint160(data)); - } - - /** - * Relayer/block builder might submit the TX with higher priorityFee, - * but the user should not pay above what he signed for. - * @param userOp - The user operation data. - */ - function gasPrice( - UserOperation calldata userOp - ) internal view returns (uint256) { - unchecked { - uint256 maxFeePerGas = userOp.maxFeePerGas; - uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; - if (maxFeePerGas == maxPriorityFeePerGas) { - //legacy mode (for networks that don't support basefee opcode) - return maxFeePerGas; - } - return min(maxFeePerGas, maxPriorityFeePerGas + block.basefee); - } - } - - /** - * Pack the user operation data into bytes for hashing. - * @param userOp - The user operation data. - */ - function pack( - UserOperation calldata userOp - ) internal pure returns (bytes memory ret) { - address sender = getSender(userOp); - uint256 nonce = userOp.nonce; - bytes32 hashInitCode = calldataKeccak(userOp.initCode); - bytes32 hashCallData = calldataKeccak(userOp.callData); - uint256 callGasLimit = userOp.callGasLimit; - uint256 verificationGasLimit = userOp.verificationGasLimit; - uint256 preVerificationGas = userOp.preVerificationGas; - uint256 maxFeePerGas = userOp.maxFeePerGas; - uint256 maxPriorityFeePerGas = userOp.maxPriorityFeePerGas; - bytes32 hashPaymasterAndData = calldataKeccak(userOp.paymasterAndData); - - return abi.encode( - sender, nonce, - hashInitCode, hashCallData, - callGasLimit, verificationGasLimit, preVerificationGas, - maxFeePerGas, maxPriorityFeePerGas, - hashPaymasterAndData - ); - } - - /** - * Hash the user operation data. - * @param userOp - The user operation data. - */ - function hash( - UserOperation calldata userOp - ) internal pure returns (bytes32) { - return keccak256(pack(userOp)); - } - - /** - * The minimum of two numbers. - * @param a - First number. - * @param b - Second number. - */ - function min(uint256 a, uint256 b) internal pure returns (uint256) { - return a < b ? a : b; - } -} diff --git a/contracts/samples/DepositPaymaster.sol b/contracts/samples/DepositPaymaster.sol index 944c9746..cabfacdb 100644 --- a/contracts/samples/DepositPaymaster.sol +++ b/contracts/samples/DepositPaymaster.sol @@ -7,6 +7,7 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../core/BasePaymaster.sol"; +import "../core/UserOperationLib.sol"; import "./IOracle.sol"; /** diff --git a/contracts/samples/VerifyingPaymaster.sol b/contracts/samples/VerifyingPaymaster.sol index 20693daa..5dcf0b15 100644 --- a/contracts/samples/VerifyingPaymaster.sol +++ b/contracts/samples/VerifyingPaymaster.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.12; /* solhint-disable no-inline-assembly */ import "../core/BasePaymaster.sol"; +import "../core/UserOperationLib.sol"; import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; /** * A sample paymaster that uses external service to decide whether to pay for the UserOp. diff --git a/contracts/samples/bls/BLSSignatureAggregator.sol b/contracts/samples/bls/BLSSignatureAggregator.sol index 60ea291b..28f60c82 100644 --- a/contracts/samples/bls/BLSSignatureAggregator.sol +++ b/contracts/samples/bls/BLSSignatureAggregator.sol @@ -4,6 +4,7 @@ pragma abicoder v2; import "../../interfaces/IAggregator.sol"; import "../../interfaces/IEntryPoint.sol"; +import "../../core/UserOperationLib.sol"; import {BLSOpen} from "./lib/BLSOpen.sol"; import "./IBLSAccount.sol"; import "./BLSHelper.sol"; diff --git a/contracts/test/TestUtil.sol b/contracts/test/TestUtil.sol index 0372ee8e..4257c3fb 100644 --- a/contracts/test/TestUtil.sol +++ b/contracts/test/TestUtil.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.12; import "../interfaces/UserOperation.sol"; +import "../core/UserOperationLib.sol"; contract TestUtil { using UserOperationLib for UserOperation;