From 7c5027ac4eee510e3654acf5ac901e1f5f61d1ab Mon Sep 17 00:00:00 2001 From: b1m0n Date: Wed, 10 Apr 2024 01:39:00 +0700 Subject: [PATCH 1/4] Init EIP-712 interfaces and libs --- contracts/eip712/IAccount.sol | 47 ++ contracts/eip712/IPaymaster.sol | 53 ++ contracts/eip712/IPaymasterFlow.sol | 17 + contracts/eip712/TestPaymaster.sol | 51 ++ contracts/eip712/TransactionHelper.sol | 172 +++++++ contracts/eip712/tmp/IAccountABI.json | 473 ++++++++++++++++++ contracts/eip712/tmp/IPaymasterABI.json | 223 +++++++++ contracts/eip712/tmp/TestPaymasterABI.json | 279 +++++++++++ contracts/eip712/tmp/TestPaymasterBytecode | 1 + contracts/libraries/ECDSA.sol | 193 +++++++ contracts/libraries/EIP712.sol | 102 ++++ contracts/openzeppelin/token/ERC20/IERC20.sol | 82 +++ .../token/ERC20/extensions/IERC20Permit.sol | 60 +++ .../token/ERC20/utils/SafeERC20.sol | 151 ++++++ contracts/openzeppelin/utils/Address.sol | 308 ++++++++++++ package-lock.json | 4 +- 16 files changed, 2214 insertions(+), 2 deletions(-) create mode 100644 contracts/eip712/IAccount.sol create mode 100644 contracts/eip712/IPaymaster.sol create mode 100644 contracts/eip712/IPaymasterFlow.sol create mode 100644 contracts/eip712/TestPaymaster.sol create mode 100644 contracts/eip712/TransactionHelper.sol create mode 100644 contracts/eip712/tmp/IAccountABI.json create mode 100644 contracts/eip712/tmp/IPaymasterABI.json create mode 100644 contracts/eip712/tmp/TestPaymasterABI.json create mode 100644 contracts/eip712/tmp/TestPaymasterBytecode create mode 100644 contracts/libraries/ECDSA.sol create mode 100644 contracts/libraries/EIP712.sol create mode 100644 contracts/openzeppelin/token/ERC20/IERC20.sol create mode 100644 contracts/openzeppelin/token/ERC20/extensions/IERC20Permit.sol create mode 100644 contracts/openzeppelin/token/ERC20/utils/SafeERC20.sol create mode 100644 contracts/openzeppelin/utils/Address.sol diff --git a/contracts/eip712/IAccount.sol b/contracts/eip712/IAccount.sol new file mode 100644 index 0000000..205d00b --- /dev/null +++ b/contracts/eip712/IAccount.sol @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import "./TransactionHelper.sol"; + +bytes4 constant ACCOUNT_VALIDATION_SUCCESS_MAGIC = IAccount.validateTransaction.selector; + +interface IAccount { + /// @notice Called by the bootloader to validate that an account agrees to process the transaction + /// (and potentially pay for it). + /// @param _txHash The hash of the transaction to be used in the explorer + /// @param _suggestedSignedHash The hash of the transaction is signed by EOAs + /// @param _transaction The transaction itself + /// @return magic The magic value that should be equal to the signature of this function + /// if the user agrees to proceed with the transaction. + /// @dev The developer should strive to preserve as many steps as possible both for valid + /// and invalid transactions as this very method is also used during the gas fee estimation + /// (without some of the necessary data, e.g. signature). + function validateTransaction( + bytes32 _txHash, + bytes32 _suggestedSignedHash, + Transaction calldata _transaction + ) external payable returns (bytes4 magic); + + function executeTransaction( + bytes32 _txHash, + bytes32 _suggestedSignedHash, + Transaction calldata _transaction + ) external payable; + + // There is no point in providing possible signed hash in the `executeTransactionFromOutside` method, + // since it typically should not be trusted. + function executeTransactionFromOutside(Transaction calldata _transaction) external payable; + + function payForTransaction( + bytes32 _txHash, + bytes32 _suggestedSignedHash, + Transaction calldata _transaction + ) external payable; + + function prepareForPaymaster( + bytes32 _txHash, + bytes32 _possibleSignedHash, + Transaction calldata _transaction + ) external payable; +} diff --git a/contracts/eip712/IPaymaster.sol b/contracts/eip712/IPaymaster.sol new file mode 100644 index 0000000..a10bed4 --- /dev/null +++ b/contracts/eip712/IPaymaster.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +import "./TransactionHelper.sol"; + +enum ExecutionResult { + Revert, + Success +} + +bytes4 constant PAYMASTER_VALIDATION_SUCCESS_MAGIC = IPaymaster.validateAndPayForPaymasterTransaction.selector; + +interface IPaymaster { + /// @dev Called by the bootloader to verify that the paymaster agrees to pay for the + /// fee for the transaction. This transaction should also send the necessary amount of funds onto the bootloader + /// address. + /// @param _txHash The hash of the transaction + /// @param _suggestedSignedHash The hash of the transaction that is signed by an EOA + /// @param _authenticationSignature The authorization proof signed by an EOA, can be used as a verifiable shortcut + /// @param _transaction The transaction itself. + /// @return magic The value that should be equal to the signature of the validateAndPayForPaymasterTransaction + /// if the paymaster agrees to pay for the transaction. + /// @return context The "context" of the transaction: an array of bytes of length at most 1024 bytes, which will be + /// passed to the `postTransaction` method of the account. + /// @dev The developer should strive to preserve as many steps as possible both for valid + /// and invalid transactions as this very method is also used during the gas fee estimation + /// (without some of the necessary data, e.g. signature). + function validateAndPayForPaymasterTransaction( + bytes32 _txHash, + bytes32 _suggestedSignedHash, + bytes memory _authenticationSignature, + Transaction calldata _transaction + ) external payable returns (bytes4 magic, bytes memory context); + + /// @dev Called by the bootloader after the execution of the transaction. Please note that + /// there is no guarantee that this method will be called at all. Unlike the original EIP4337, + /// this method won't be called if the transaction execution results in out-of-gas. + /// @param _context, the context of the execution, returned by the "validateAndPayForPaymasterTransaction" method. + /// @param _transaction, the users' transaction. + /// @param _txResult, the result of the transaction execution (success or failure). + /// @param _maxRefundedGas, the upper bound on the amout of gas that could be refunded to the paymaster. + /// @dev The exact amount refunded depends on the gas spent by the "postOp" itself and so the developers should + /// take that into account. + function postTransaction( + bytes calldata _context, + Transaction calldata _transaction, + bytes32 _txHash, + bytes32 _suggestedSignedHash, + ExecutionResult _txResult, + uint256 _maxRefundedGas + ) external payable; +} diff --git a/contracts/eip712/IPaymasterFlow.sol b/contracts/eip712/IPaymasterFlow.sol new file mode 100644 index 0000000..7bfcd24 --- /dev/null +++ b/contracts/eip712/IPaymasterFlow.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.20; + +/** + * @author Matter Labs + * @custom:security-contact security@matterlabs.dev + * @dev The interface that is used for encoding/decoding of + * different types of paymaster flows. + * @notice This is NOT an interface to be implementated + * by contracts. It is just used for encoding. + */ +interface IPaymasterFlow { + function general(bytes calldata input) external; + + function approvalBased(address _token, uint256 _minAllowance, bytes calldata _innerInput) external; +} diff --git a/contracts/eip712/TestPaymaster.sol b/contracts/eip712/TestPaymaster.sol new file mode 100644 index 0000000..ea15994 --- /dev/null +++ b/contracts/eip712/TestPaymaster.sol @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "./IPaymaster.sol"; +import "../libraries/EIP712.sol"; +import "../libraries/ECDSA.sol"; + +contract TestPaymaster is IPaymaster, EIP712 { + struct SignData { + bytes32 suggestedSignedHash; + } + + address public owner; + + constructor(string memory name, string memory version) EIP712(name, version) { + owner = msg.sender; + } + + function signDataTypeHash() internal pure returns (bytes32) { + return keccak256("SignData(bytes32 suggestedSignedHash)"); + } + + function signDataMessage(bytes32 suggestedSignedHash) internal view returns (bytes32) { + return ECDSA.toTypedDataHash(_domainSeparatorV4(), keccak256(abi.encode( + signDataTypeHash(), + suggestedSignedHash + ))); + } + + function validateAndPayForPaymasterTransaction( + bytes32 _txHash, + bytes32 _suggestedSignedHash, + bytes memory _authenticationSignature, + Transaction calldata _transaction + ) external payable returns (bytes4 magic, bytes memory context) { + address recoverAddress = ECDSA.recover(signDataMessage(_suggestedSignedHash), _authenticationSignature); + require(recoverAddress == owner, "Paymaster: Unauthorized signature"); + return (PAYMASTER_VALIDATION_SUCCESS_MAGIC, context); + } + + function postTransaction( + bytes calldata _context, + Transaction calldata _transaction, + bytes32 _txHash, + bytes32 _suggestedSignedHash, + ExecutionResult _txResult, + uint256 _maxRefundedGas + ) external payable { + + } +} \ No newline at end of file diff --git a/contracts/eip712/TransactionHelper.sol b/contracts/eip712/TransactionHelper.sol new file mode 100644 index 0000000..816b6ac --- /dev/null +++ b/contracts/eip712/TransactionHelper.sol @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.20; + +import "../openzeppelin/token/ERC20/IERC20.sol"; +import "../openzeppelin/token/ERC20/utils/SafeERC20.sol"; + +import "./IPaymasterFlow.sol"; + +/// @dev The type id of U2U's EIP-712-signed transaction. +uint8 constant EIP_712_TX_TYPE = 0x71; + +/// @dev The type id of legacy transactions. +uint8 constant LEGACY_TX_TYPE = 0x0; +/// @dev The type id of legacy transactions. +uint8 constant EIP_2930_TX_TYPE = 0x01; +/// @dev The type id of EIP1559 transactions. +uint8 constant EIP_1559_TX_TYPE = 0x02; + +address constant U2U_TOKEN = address(0xA99cf32e9aAa700f9E881BA9BF2C57A211ae94df); + +/// @notice Structure used to represent a U2U transaction. +struct Transaction { + // The type of the transaction. + uint256 txType; + // The caller. + uint256 from; + // The callee. + uint256 to; + // The gasLimit to pass with the transaction. + // It has the same meaning as Ethereum's gasLimit. + uint256 gasLimit; + // The maximum amount of gas the user is willing to pay for a byte of pubdata. + uint256 gasPerPubdataByteLimit; + // The maximum fee per gas that the user is willing to pay. + // It is akin to EIP1559's maxFeePerGas. + uint256 maxFeePerGas; + // The maximum priority fee per gas that the user is willing to pay. + // It is akin to EIP1559's maxPriorityFeePerGas. + uint256 maxPriorityFeePerGas; + // The transaction's paymaster. If there is no paymaster, it is equal to 0. + uint256 paymaster; + // The nonce of the transaction. + uint256 nonce; + // The value to pass with the transaction. + uint256 value; + // In the future, we might want to add some + // new fields to the struct. The `txData` struct + // is to be passed to account and any changes to its structure + // would mean a breaking change to these accounts. In order to prevent this, + // we should keep some fields as "reserved". + // It is also recommended that their length is fixed, since + // it would allow easier proof integration (in case we will need + // some special circuit for preprocessing transactions). + uint256[4] reserved; + // The transaction's calldata. + bytes data; + // The signature of the transaction. + bytes signature; + // The input to the paymaster. + bytes paymasterInput; +} + +/** + * @author Matter Labs + * @custom:security-contact security@matterlabs.dev + * @notice Library is used to help custom accounts to work with common methods for the Transaction type. + */ +library TransactionHelper { + using SafeERC20 for IERC20; + + /// @notice The EIP-712 typehash for the contract's domain + bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId)"); + + bytes32 constant EIP712_TRANSACTION_TYPE_HASH = + keccak256( + "Transaction(uint256 txType,uint256 from,uint256 to,uint256 gasLimit,uint256 gasPerPubdataByteLimit,uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,uint256 paymaster,uint256 nonce,uint256 value,bytes data,bytes paymasterInput)" + ); + + /// @notice Whether the token is Ethereum. + /// @param _addr The address of the token + /// @return `true` or `false` based on whether the token is Ether. + /// @dev This method assumes that address is Ether either if the address is 0 (for convenience) + /// or if the address is the address of the L2EthToken system contract. + function isU2UToken(uint256 _addr) internal pure returns (bool) { + return _addr == uint256(uint160(address(U2U_TOKEN))) || _addr == 0; + } + + /// @notice Calculate the suggested signed hash of the transaction, + /// i.e. the hash that is signed by EOAs and is recommended to be signed by other accounts. + function encodeHash(Transaction calldata _transaction) internal view returns (bytes32 resultHash) { + if (_transaction.txType == EIP_712_TX_TYPE) { + resultHash = _encodeHashEIP712Transaction(_transaction); + } else { + // Currently no other transaction types are supported. + // Any new transaction types will be processed in a similar manner. + revert("Encoding unsupported tx"); + } + } + + /// @notice Encode hash of the U2U native transaction type. + /// @return keccak256 hash of the EIP-712 encoded representation of transaction + function _encodeHashEIP712Transaction(Transaction calldata _transaction) private view returns (bytes32) { + bytes32 structHash = keccak256( + abi.encode( + EIP712_TRANSACTION_TYPE_HASH, + _transaction.txType, + _transaction.from, + _transaction.to, + _transaction.gasLimit, + _transaction.gasPerPubdataByteLimit, + _transaction.maxFeePerGas, + _transaction.maxPriorityFeePerGas, + _transaction.paymaster, + _transaction.nonce, + _transaction.value, + keccak256(_transaction.data), + keccak256(_transaction.paymasterInput) + ) + ); + + bytes32 domainSeparator = keccak256( + abi.encode(EIP712_DOMAIN_TYPEHASH, keccak256("U2U"), keccak256("2"), block.chainid) + ); + + return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + } + + /// @notice Processes the common paymaster flows, e.g. setting proper allowance + /// for tokens, etc. For more information on the expected behavior, check out + /// the "Paymaster flows" section in the documentation. + function processPaymasterInput(Transaction calldata _transaction) internal { + require(_transaction.paymasterInput.length >= 4, "The standard paymaster input must be at least 4 bytes long"); + + bytes4 paymasterInputSelector = bytes4(_transaction.paymasterInput[0:4]); + if (paymasterInputSelector == IPaymasterFlow.approvalBased.selector) { + require( + _transaction.paymasterInput.length >= 68, + "The approvalBased paymaster input must be at least 68 bytes long" + ); + + // While the actual data consists of address, uint256 and bytes data, + // the data is needed only for the paymaster, so we ignore it here for the sake of optimization + (address token, uint256 minAllowance) = abi.decode(_transaction.paymasterInput[4:68], (address, uint256)); + address paymaster = address(uint160(_transaction.paymaster)); + + uint256 currentAllowance = IERC20(token).allowance(address(this), paymaster); + if (currentAllowance < minAllowance) { + // Some tokens, e.g. USDT require that the allowance is firsty set to zero + // and only then updated to the new value. + + IERC20(token).safeApprove(paymaster, 0); + IERC20(token).safeApprove(paymaster, minAllowance); + } + } else if (paymasterInputSelector == IPaymasterFlow.general.selector) { + // Do nothing. general(bytes) paymaster flow means that the paymaster must interpret these bytes on his own. + } else { + revert("Unsupported paymaster flow"); + } + } + + // Returns the balance required to process the transaction. + function totalRequiredBalance(Transaction calldata _transaction) internal pure returns (uint256 requiredBalance) { + if (address(uint160(_transaction.paymaster)) != address(0)) { + // Paymaster pays for the fee + requiredBalance = _transaction.value; + } else { + // The user should have enough balance for both the fee and the value of the transaction + requiredBalance = _transaction.maxFeePerGas * _transaction.gasLimit + _transaction.value; + } + } +} diff --git a/contracts/eip712/tmp/IAccountABI.json b/contracts/eip712/tmp/IAccountABI.json new file mode 100644 index 0000000..7d20a76 --- /dev/null +++ b/contracts/eip712/tmp/IAccountABI.json @@ -0,0 +1,473 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_suggestedSignedHash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + } + ], + "name": "executeTransaction", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + } + ], + "name": "executeTransactionFromOutside", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_suggestedSignedHash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + } + ], + "name": "payForTransaction", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_possibleSignedHash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + } + ], + "name": "prepareForPaymaster", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_suggestedSignedHash", + "type": "bytes32" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + } + ], + "name": "validateTransaction", + "outputs": [ + { + "internalType": "bytes4", + "name": "magic", + "type": "bytes4" + } + ], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/contracts/eip712/tmp/IPaymasterABI.json b/contracts/eip712/tmp/IPaymasterABI.json new file mode 100644 index 0000000..419e803 --- /dev/null +++ b/contracts/eip712/tmp/IPaymasterABI.json @@ -0,0 +1,223 @@ +[ + { + "inputs": [ + { + "internalType": "bytes", + "name": "_context", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_suggestedSignedHash", + "type": "bytes32" + }, + { + "internalType": "enum ExecutionResult", + "name": "_txResult", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "_maxRefundedGas", + "type": "uint256" + } + ], + "name": "postTransaction", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_suggestedSignedHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_authenticationSignature", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + } + ], + "name": "validateAndPayForPaymasterTransaction", + "outputs": [ + { + "internalType": "bytes4", + "name": "magic", + "type": "bytes4" + }, + { + "internalType": "bytes", + "name": "context", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/contracts/eip712/tmp/TestPaymasterABI.json b/contracts/eip712/tmp/TestPaymasterABI.json new file mode 100644 index 0000000..6e094f0 --- /dev/null +++ b/contracts/eip712/tmp/TestPaymasterABI.json @@ -0,0 +1,279 @@ +[ + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "version", + "type": "string" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "ECDSAInvalidSignature", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "length", + "type": "uint256" + } + ], + "name": "ECDSAInvalidSignatureLength", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "ECDSAInvalidSignatureS", + "type": "error" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes", + "name": "_context", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_suggestedSignedHash", + "type": "bytes32" + }, + { + "internalType": "enum ExecutionResult", + "name": "_txResult", + "type": "uint8" + }, + { + "internalType": "uint256", + "name": "_maxRefundedGas", + "type": "uint256" + } + ], + "name": "postTransaction", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "_txHash", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "_suggestedSignedHash", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "_authenticationSignature", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "txType", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "from", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "to", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "gasPerPubdataByteLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maxPriorityFeePerGas", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "paymaster", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + }, + { + "internalType": "uint256[4]", + "name": "reserved", + "type": "uint256[4]" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "paymasterInput", + "type": "bytes" + } + ], + "internalType": "struct Transaction", + "name": "_transaction", + "type": "tuple" + } + ], + "name": "validateAndPayForPaymasterTransaction", + "outputs": [ + { + "internalType": "bytes4", + "name": "magic", + "type": "bytes4" + }, + { + "internalType": "bytes", + "name": "context", + "type": "bytes" + } + ], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/contracts/eip712/tmp/TestPaymasterBytecode b/contracts/eip712/tmp/TestPaymasterBytecode new file mode 100644 index 0000000..457fa7a --- /dev/null +++ b/contracts/eip712/tmp/TestPaymasterBytecode @@ -0,0 +1 @@ +61014060405234801562000011575f80fd5b506040516200123b3803806200123b833981810160405281019062000037919062000314565b81815f828051906020012090505f828051906020012090505f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f90508260e08181525050816101008181525050620000946200013e60201b60201c565b60a08181525050620000ae8184846200014560201b60201c565b608081815250503073ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff16815250508061012081815250505050505050335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505062000469565b5f46905090565b5f838383620001596200013e60201b60201c565b30604051602001620001709594939291906200040e565b6040516020818303038152906040528051906020012090509392505050565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620001f082620001a8565b810181811067ffffffffffffffff82111715620002125762000211620001b8565b5b80604052505050565b5f620002266200018f565b9050620002348282620001e5565b919050565b5f67ffffffffffffffff821115620002565762000255620001b8565b5b6200026182620001a8565b9050602081019050919050565b5f5b838110156200028d57808201518184015260208101905062000270565b5f8484015250505050565b5f620002ae620002a88462000239565b6200021b565b905082815260208101848484011115620002cd57620002cc620001a4565b5b620002da8482856200026e565b509392505050565b5f82601f830112620002f957620002f8620001a0565b5b81516200030b84826020860162000298565b91505092915050565b5f80604083850312156200032d576200032c62000198565b5b5f83015167ffffffffffffffff8111156200034d576200034c6200019c565b5b6200035b85828601620002e2565b925050602083015167ffffffffffffffff8111156200037f576200037e6200019c565b5b6200038d85828601620002e2565b9150509250929050565b5f819050919050565b620003ab8162000397565b82525050565b5f819050919050565b620003c581620003b1565b82525050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f620003f682620003cb565b9050919050565b6200040881620003ea565b82525050565b5f60a082019050620004235f830188620003a0565b620004326020830187620003a0565b620004416040830186620003a0565b620004506060830185620003ba565b6200045f6080830184620003fd565b9695505050505050565b60805160a05160c05160e0516101005161012051610d8a620004b15f395f6102b901525f6102fb01525f6102da01525f61020801525f61025e01525f61028e0152610d8a5ff3fe608060405260043610610033575f3560e01c8063391c91d2146100375780638da5cb5b146100685780639f3cb0b014610092575b5f80fd5b610051600480360381019061004c9190610814565b6100ae565b60405161005f929190610964565b60405180910390f35b348015610073575f80fd5b5061007c610167565b60405161008991906109d1565b60405180910390f35b6100ac60048036038101906100a79190610a9d565b61018a565b005b5f60605f6100c46100be87610193565b866101db565b90505f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610153576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014a90610be3565b60405180910390fd5b63391c91d260e01b92505094509492505050565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b50505050505050565b5f6101d461019f610205565b6101a7610325565b846040516020016101b9929190610c10565b6040516020818303038152906040528051906020012061034c565b9050919050565b5f805f806101e9868661038c565b9250925092506101f982826103e1565b82935050505092915050565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614801561028757507f0000000000000000000000000000000000000000000000000000000000000000610285610543565b145b156102b4577f00000000000000000000000000000000000000000000000000000000000000009050610322565b61031f7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061054a565b90505b90565b5f7f2bb156903cb7269fcabf8c90e4a20f3c4eaf1dde6b407dbc50a24ac21c9fab69905090565b5f6040517f190100000000000000000000000000000000000000000000000000000000000081528360028201528260228201526042812091505092915050565b5f805f60418451036103cc575f805f602087015192506040870151915060608701515f1a90506103be8882858561058a565b9550955095505050506103da565b5f600285515f1b9250925092505b9250925092565b5f60038111156103f4576103f3610c37565b5b82600381111561040757610406610c37565b5b031561053f576001600381111561042157610420610c37565b5b82600381111561043457610433610c37565b5b0361046b576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600381111561047f5761047e610c37565b5b82600381111561049257610491610c37565b5b036104d657805f1c6040517ffce698f70000000000000000000000000000000000000000000000000000000081526004016104cd9190610c73565b60405180910390fd5b6003808111156104e9576104e8610c37565b5b8260038111156104fc576104fb610c37565b5b0361053e57806040517fd78bce0c0000000000000000000000000000000000000000000000000000000081526004016105359190610c8c565b60405180910390fd5b5b5050565b5f46905090565b5f838383610556610543565b3060405160200161056b959493929190610ca5565b6040516020818303038152906040528051906020012090509392505050565b5f805f7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0845f1c11156105c6575f600385925092509250610667565b5f6001888888886040515f81526020016040526040516105e99493929190610d11565b6020604051602081039080840390855afa158015610609573d5f803e3d5ffd5b5050506020604051035190505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361065a575f60015f801b93509350935050610667565b805f805f1b935093509350505b9450945094915050565b5f604051905090565b5f80fd5b5f80fd5b5f819050919050565b61069481610682565b811461069e575f80fd5b50565b5f813590506106af8161068b565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610703826106bd565b810181811067ffffffffffffffff82111715610722576107216106cd565b5b80604052505050565b5f610734610671565b905061074082826106fa565b919050565b5f67ffffffffffffffff82111561075f5761075e6106cd565b5b610768826106bd565b9050602081019050919050565b828183375f83830152505050565b5f61079561079084610745565b61072b565b9050828152602081018484840111156107b1576107b06106b9565b5b6107bc848285610775565b509392505050565b5f82601f8301126107d8576107d76106b5565b5b81356107e8848260208601610783565b91505092915050565b5f80fd5b5f610220828403121561080b5761080a6107f1565b5b81905092915050565b5f805f806080858703121561082c5761082b61067a565b5b5f610839878288016106a1565b945050602061084a878288016106a1565b935050604085013567ffffffffffffffff81111561086b5761086a61067e565b5b610877878288016107c4565b925050606085013567ffffffffffffffff8111156108985761089761067e565b5b6108a4878288016107f5565b91505092959194509250565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6108e4816108b0565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610921578082015181840152602081019050610906565b5f8484015250505050565b5f610936826108ea565b61094081856108f4565b9350610950818560208601610904565b610959816106bd565b840191505092915050565b5f6040820190506109775f8301856108db565b8181036020830152610989818461092c565b90509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6109bb82610992565b9050919050565b6109cb816109b1565b82525050565b5f6020820190506109e45f8301846109c2565b92915050565b5f80fd5b5f80fd5b5f8083601f840112610a0757610a066106b5565b5b8235905067ffffffffffffffff811115610a2457610a236109ea565b5b602083019150836001820283011115610a4057610a3f6109ee565b5b9250929050565b60028110610a53575f80fd5b50565b5f81359050610a6481610a47565b92915050565b5f819050919050565b610a7c81610a6a565b8114610a86575f80fd5b50565b5f81359050610a9781610a73565b92915050565b5f805f805f805f60c0888a031215610ab857610ab761067a565b5b5f88013567ffffffffffffffff811115610ad557610ad461067e565b5b610ae18a828b016109f2565b9750975050602088013567ffffffffffffffff811115610b0457610b0361067e565b5b610b108a828b016107f5565b9550506040610b218a828b016106a1565b9450506060610b328a828b016106a1565b9350506080610b438a828b01610a56565b92505060a0610b548a828b01610a89565b91505092959891949750929550565b5f82825260208201905092915050565b7f5061796d61737465723a20556e617574686f72697a6564207369676e617475725f8201527f6500000000000000000000000000000000000000000000000000000000000000602082015250565b5f610bcd602183610b63565b9150610bd882610b73565b604082019050919050565b5f6020820190508181035f830152610bfa81610bc1565b9050919050565b610c0a81610682565b82525050565b5f604082019050610c235f830185610c01565b610c306020830184610c01565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b610c6d81610a6a565b82525050565b5f602082019050610c865f830184610c64565b92915050565b5f602082019050610c9f5f830184610c01565b92915050565b5f60a082019050610cb85f830188610c01565b610cc56020830187610c01565b610cd26040830186610c01565b610cdf6060830185610c64565b610cec60808301846109c2565b9695505050505050565b5f60ff82169050919050565b610d0b81610cf6565b82525050565b5f608082019050610d245f830187610c01565b610d316020830186610d02565b610d3e6040830185610c01565b610d4b6060830184610c01565b9594505050505056fea2646970667358221220d2a7d277607bf46c3ee79ab177bb58aabc4fccb2381b50fec4e37ec7e100e69a64736f6c63430008140033 \ No newline at end of file diff --git a/contracts/libraries/ECDSA.sol b/contracts/libraries/ECDSA.sol new file mode 100644 index 0000000..93f7380 --- /dev/null +++ b/contracts/libraries/ECDSA.sol @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: MIT + +pragma solidity >=0.6.2; + +/** + * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. + * + * These functions can be used to verify that a message was signed by the holder + * of the private keys of a given address. + */ +library ECDSA { + enum RecoverError { + NoError, + InvalidSignature, + InvalidSignatureLength, + InvalidSignatureS + } + + /** + * @dev The signature derives the `address(0)`. + */ + error ECDSAInvalidSignature(); + + /** + * @dev The signature has an invalid length. + */ + error ECDSAInvalidSignatureLength(uint256 length); + + /** + * @dev The signature has an S value that is in the upper half order. + */ + error ECDSAInvalidSignatureS(bytes32 s); + + /** + * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not + * return address(0) without also returning an error description. Errors are documented using an enum (error type) + * and a bytes32 providing additional information about the error. + * + * If no error is returned, then the address can be used for verification purposes. + * + * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. + * + * Documentation for signature generation: + * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] + * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] + */ + function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { + if (signature.length == 65) { + bytes32 r; + bytes32 s; + uint8 v; + // ecrecover takes the signature parameters, and the only way to get them + // currently is to use assembly. + /// @solidity memory-safe-assembly + assembly { + r := mload(add(signature, 0x20)) + s := mload(add(signature, 0x40)) + v := byte(0, mload(add(signature, 0x60))) + } + return tryRecover(hash, v, r, s); + } else { + return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); + } + } + + /** + * @dev Returns the address that signed a hashed message (`hash`) with + * `signature`. This address can then be used for verification purposes. + * + * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: + * this function rejects them by requiring the `s` value to be in the lower + * half order, and the `v` value to be either 27 or 28. + * + * IMPORTANT: `hash` _must_ be the result of a hash operation for the + * verification to be secure: it is possible to craft signatures that + * recover to arbitrary addresses for non-hashed data. A safe way to ensure + * this is by receiving a hash of the original message (which may otherwise + * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. + */ + function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { + (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); + _throwError(error, errorArg); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. + * + * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] + */ + function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { + unchecked { + bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); + // We do not check for an overflow here since the shift operation results in 0 or 1. + uint8 v = uint8((uint256(vs) >> 255) + 27); + return tryRecover(hash, v, r, s); + } + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. + */ + function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { + (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); + _throwError(error, errorArg); + return recovered; + } + + /** + * @dev Overload of {ECDSA-tryRecover} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function tryRecover( + bytes32 hash, + uint8 v, + bytes32 r, + bytes32 s + ) internal pure returns (address, RecoverError, bytes32) { + // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature + // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines + // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most + // signatures from current libraries generate a unique signature with an s-value in the lower half order. + // + // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value + // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or + // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept + // these malleable signatures as well. + if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { + return (address(0), RecoverError.InvalidSignatureS, s); + } + + // If the signature is valid (and not malleable), return the signer address + address signer = ecrecover(hash, v, r, s); + if (signer == address(0)) { + return (address(0), RecoverError.InvalidSignature, bytes32(0)); + } + + return (signer, RecoverError.NoError, bytes32(0)); + } + + /** + * @dev Overload of {ECDSA-recover} that receives the `v`, + * `r` and `s` signature fields separately. + */ + function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { + (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s); + _throwError(error, errorArg); + return recovered; + } + + /** + * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. + */ + function _throwError(RecoverError error, bytes32 errorArg) private pure { + if (error == RecoverError.NoError) { + return; // no error: do nothing + } else if (error == RecoverError.InvalidSignature) { + revert ECDSAInvalidSignature(); + } else if (error == RecoverError.InvalidSignatureLength) { + revert ECDSAInvalidSignatureLength(uint256(errorArg)); + } else if (error == RecoverError.InvalidSignatureS) { + revert ECDSAInvalidSignatureS(errorArg); + } + } + + /** + * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). + * + * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with + * `\x19\x01` and hashing the result. It corresponds to the hash signed by the + * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. + * + * See {ECDSA-recover}. + */ + function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { + /// @solidity memory-safe-assembly + assembly { + let ptr := mload(0x40) + mstore(ptr, hex"19_01") + mstore(add(ptr, 0x02), domainSeparator) + mstore(add(ptr, 0x22), structHash) + digest := keccak256(ptr, 0x42) + } + } +} \ No newline at end of file diff --git a/contracts/libraries/EIP712.sol b/contracts/libraries/EIP712.sol new file mode 100644 index 0000000..cc6a856 --- /dev/null +++ b/contracts/libraries/EIP712.sol @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.6.0; + +/** + * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. + * + * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, + * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding + * they need in their contracts using a combination of `abi.encode` and `keccak256`. + * + * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding + * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA + * ({_hashTypedDataV4}). + * + * The implementation of the domain separator was designed to be as efficient as possible while still properly updating + * the chain id to protect against replay attacks on an eventual fork of the chain. + * + * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method + * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. + * + * _Available since v3.4._ + */ +abstract contract EIP712 { + /* solhint-disable var-name-mixedcase */ + // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to + // invalidate the cached domain separator if the chain id changes. + bytes32 private immutable _CACHED_DOMAIN_SEPARATOR; + uint256 private immutable _CACHED_CHAIN_ID; + address private immutable _CACHED_THIS; + + bytes32 private immutable _HASHED_NAME; + bytes32 private immutable _HASHED_VERSION; + bytes32 private immutable _TYPE_HASH; + + /* solhint-enable var-name-mixedcase */ + + /** + * @dev Initializes the domain separator and parameter caches. + * + * The meaning of `name` and `version` is specified in + * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: + * + * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. + * - `version`: the current major version of the signing domain. + * + * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart + * contract upgrade]. + */ + constructor(string memory name, string memory version) internal { + bytes32 hashedName = keccak256(bytes(name)); + bytes32 hashedVersion = keccak256(bytes(version)); + bytes32 typeHash = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); + _HASHED_NAME = hashedName; + _HASHED_VERSION = hashedVersion; + _CACHED_CHAIN_ID = _getChainId(); + _CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(typeHash, hashedName, hashedVersion); + _CACHED_THIS = address(this); + _TYPE_HASH = typeHash; + } + + /** + * @dev Returns the domain separator for the current chain. + */ + function _domainSeparatorV4() internal view returns (bytes32) { + if (address(this) == _CACHED_THIS && _getChainId() == _CACHED_CHAIN_ID) { + return _CACHED_DOMAIN_SEPARATOR; + } else { + return _buildDomainSeparator(_TYPE_HASH, _HASHED_NAME, _HASHED_VERSION); + } + } + + function _buildDomainSeparator(bytes32 typeHash, bytes32 nameHash, bytes32 versionHash) private view returns (bytes32) { + return keccak256(abi.encode(typeHash, nameHash, versionHash, _getChainId(), address(this))); + } + + /** + * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this + * function returns the hash of the fully encoded EIP712 message for this domain. + * + * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: + * + * ```solidity + * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( + * keccak256("Mail(address to,string contents)"), + * mailTo, + * keccak256(bytes(mailContents)) + * ))); + * address signer = ECDSA.recover(digest, signature); + * ``` + */ + function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { + return keccak256(abi.encodePacked("\x19\x01", _domainSeparatorV4(), structHash)); + } + + function _getChainId() private view returns (uint256 chainId) { + this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 + // solhint-disable-next-line no-inline-assembly + assembly { + chainId := chainid() + } + } +} diff --git a/contracts/openzeppelin/token/ERC20/IERC20.sol b/contracts/openzeppelin/token/ERC20/IERC20.sol new file mode 100644 index 0000000..b816bfe --- /dev/null +++ b/contracts/openzeppelin/token/ERC20/IERC20.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 standard as defined in the EIP. + */ +interface IERC20 { + /** + * @dev Emitted when `value` tokens are moved from one account (`from`) to + * another (`to`). + * + * Note that `value` may be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); + + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); + + /** + * @dev Returns the amount of tokens in existence. + */ + function totalSupply() external view returns (uint256); + + /** + * @dev Returns the amount of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); + + /** + * @dev Moves `amount` tokens from the caller's account to `to`. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 amount) external returns (bool); + + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + */ + function allowance(address owner, address spender) external view returns (uint256); + + /** + * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 amount) external returns (bool); + + /** + * @dev Moves `amount` tokens from `from` to `to` using the + * allowance mechanism. `amount` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 amount + ) external returns (bool); +} diff --git a/contracts/openzeppelin/token/ERC20/extensions/IERC20Permit.sol b/contracts/openzeppelin/token/ERC20/extensions/IERC20Permit.sol new file mode 100644 index 0000000..bb43e53 --- /dev/null +++ b/contracts/openzeppelin/token/ERC20/extensions/IERC20Permit.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Permit.sol) + +pragma solidity ^0.8.0; + +/** + * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in + * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. + * + * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by + * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't + * need to send a transaction, and thus is not required to hold Ether at all. + */ +interface IERC20Permit { + /** + * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, + * given ``owner``'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {Approval} event. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {nonces}). + * + * For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP + * section]. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases ``owner``'s nonce by one. This + * prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); + + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); +} diff --git a/contracts/openzeppelin/token/ERC20/utils/SafeERC20.sol b/contracts/openzeppelin/token/ERC20/utils/SafeERC20.sol new file mode 100644 index 0000000..4a07fff --- /dev/null +++ b/contracts/openzeppelin/token/ERC20/utils/SafeERC20.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) + +pragma solidity ^0.8.0; + +import "../IERC20.sol"; +import "../extensions/IERC20Permit.sol"; +import "../../../utils/Address.sol"; + +/** + * @title SafeERC20 + * @dev Wrappers around ERC20 operations that throw on failure (when the token + * contract returns false). Tokens that return no value (and instead revert or + * throw on failure) are also supported, non-reverting calls are assumed to be + * successful. + * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, + * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. + */ +library SafeERC20 { + using Address for address; + + function safeTransfer( + IERC20 token, + address to, + uint256 value + ) internal { + _callOptionalReturn( + token, + abi.encodeWithSelector(token.transfer.selector, to, value) + ); + } + + function safeTransferFrom( + IERC20 token, + address from, + address to, + uint256 value + ) internal { + _callOptionalReturn( + token, + abi.encodeWithSelector(token.transferFrom.selector, from, to, value) + ); + } + + /** + * @dev Deprecated. This function has issues similar to the ones found in + * {IERC20-approve}, and its usage is discouraged. + * + * Whenever possible, use {safeIncreaseAllowance} and + * {safeDecreaseAllowance} instead. + */ + function safeApprove( + IERC20 token, + address spender, + uint256 value + ) internal { + // safeApprove should only be called when setting an initial allowance, + // or when resetting it to zero. To increase and decrease it, use + // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' + require( + (value == 0) || (token.allowance(address(this), spender) == 0), + "SafeERC20: approve from non-zero to non-zero allowance" + ); + _callOptionalReturn( + token, + abi.encodeWithSelector(token.approve.selector, spender, value) + ); + } + + function safeIncreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + uint256 newAllowance = token.allowance(address(this), spender) + value; + _callOptionalReturn( + token, + abi.encodeWithSelector( + token.approve.selector, + spender, + newAllowance + ) + ); + } + + function safeDecreaseAllowance( + IERC20 token, + address spender, + uint256 value + ) internal { + unchecked { + uint256 oldAllowance = token.allowance(address(this), spender); + require( + oldAllowance >= value, + "SafeERC20: decreased allowance below zero" + ); + uint256 newAllowance = oldAllowance - value; + _callOptionalReturn( + token, + abi.encodeWithSelector( + token.approve.selector, + spender, + newAllowance + ) + ); + } + } + + function safePermit( + IERC20Permit token, + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) internal { + uint256 nonceBefore = token.nonces(owner); + token.permit(owner, spender, value, deadline, v, r, s); + uint256 nonceAfter = token.nonces(owner); + require( + nonceAfter == nonceBefore + 1, + "SafeERC20: permit did not succeed" + ); + } + + /** + * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement + * on the return value: the return value is optional (but if data is returned, it must not be false). + * @param token The token targeted by the call. + * @param data The call data (encoded using abi.encode or one of its variants). + */ + function _callOptionalReturn(IERC20 token, bytes memory data) private { + // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since + // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that + // the target address contains contract code and also asserts for success in the low-level call. + + bytes memory returndata = address(token).functionCall( + data, + "SafeERC20: low-level call failed" + ); + if (returndata.length > 0) { + // Return data is optional + require( + abi.decode(returndata, (bool)), + "SafeERC20: ERC20 operation did not succeed" + ); + } + } +} diff --git a/contracts/openzeppelin/utils/Address.sol b/contracts/openzeppelin/utils/Address.sol new file mode 100644 index 0000000..7a7d2d5 --- /dev/null +++ b/contracts/openzeppelin/utils/Address.sol @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) + +pragma solidity ^0.8.1; + +/** + * @dev Collection of functions related to the address type + */ +library Address { + /** + * @dev Returns true if `account` is a contract. + * + * [IMPORTANT] + * ==== + * It is unsafe to assume that an address for which this function returns + * false is an externally-owned account (EOA) and not a contract. + * + * Among others, `isContract` will return false for the following + * types of addresses: + * + * - an externally-owned account + * - a contract in construction + * - an address where a contract will be created + * - an address where a contract lived, but was destroyed + * ==== + * + * [IMPORTANT] + * ==== + * You shouldn't rely on `isContract` to protect against flash loan attacks! + * + * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets + * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract + * constructor. + * ==== + */ + function isContract(address account) internal view returns (bool) { + // This method relies on extcodesize/address.code.length, which returns 0 + // for contracts in construction, since the code is only stored at the end + // of the constructor execution. + + return account.code.length > 0; + } + + /** + * @dev Replacement for Solidity's `transfer`: sends `amount` wei to + * `recipient`, forwarding all available gas and reverting on errors. + * + * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost + * of certain opcodes, possibly making contracts go over the 2300 gas limit + * imposed by `transfer`, making them unable to receive funds via + * `transfer`. {sendValue} removes this limitation. + * + * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. + * + * IMPORTANT: because control is transferred to `recipient`, care must be + * taken to not create reentrancy vulnerabilities. Consider using + * {ReentrancyGuard} or the + * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. + */ + function sendValue(address payable recipient, uint256 amount) internal { + require( + address(this).balance >= amount, + "Address: insufficient balance" + ); + + (bool success, ) = recipient.call{value: amount}(""); + require( + success, + "Address: unable to send value, recipient may have reverted" + ); + } + + /** + * @dev Performs a Solidity function call using a low level `call`. A + * plain `call` is an unsafe replacement for a function call: use this + * function instead. + * + * If `target` reverts with a revert reason, it is bubbled up by this + * function (like regular Solidity function calls). + * + * Returns the raw returned data. To convert to the expected return value, + * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. + * + * Requirements: + * + * - `target` must be a contract. + * - calling `target` with `data` must not revert. + * + * _Available since v3.1._ + */ + function functionCall(address target, bytes memory data) + internal + returns (bytes memory) + { + return + functionCallWithValue( + target, + data, + 0, + "Address: low-level call failed" + ); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with + * `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but also transferring `value` wei to `target`. + * + * Requirements: + * + * - the calling contract must have an ETH balance of at least `value`. + * - the called Solidity function must be `payable`. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return + functionCallWithValue( + target, + data, + value, + "Address: low-level call with value failed" + ); + } + + /** + * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but + * with `errorMessage` as a fallback revert reason when `target` reverts. + * + * _Available since v3.1._ + */ + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require( + address(this).balance >= value, + "Address: insufficient balance for call" + ); + (bool success, bytes memory returndata) = target.call{value: value}( + data + ); + return + verifyCallResultFromTarget( + target, + success, + returndata, + errorMessage + ); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall(address target, bytes memory data) + internal + view + returns (bytes memory) + { + return + functionStaticCall( + target, + data, + "Address: low-level static call failed" + ); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a static call. + * + * _Available since v3.3._ + */ + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + (bool success, bytes memory returndata) = target.staticcall(data); + return + verifyCallResultFromTarget( + target, + success, + returndata, + errorMessage + ); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall(address target, bytes memory data) + internal + returns (bytes memory) + { + return + functionDelegateCall( + target, + data, + "Address: low-level delegate call failed" + ); + } + + /** + * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], + * but performing a delegate call. + * + * _Available since v3.4._ + */ + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + (bool success, bytes memory returndata) = target.delegatecall(data); + return + verifyCallResultFromTarget( + target, + success, + returndata, + errorMessage + ); + } + + /** + * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling + * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. + * + * _Available since v4.8._ + */ + function verifyCallResultFromTarget( + address target, + bool success, + bytes memory returndata, + string memory errorMessage + ) internal view returns (bytes memory) { + if (success) { + if (returndata.length == 0) { + // only check isContract if the call was successful and the return data is empty + // otherwise we already know that it was a contract + require(isContract(target), "Address: call to non-contract"); + } + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + /** + * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the + * revert reason or using the provided one. + * + * _Available since v4.3._ + */ + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + function _revert(bytes memory returndata, string memory errorMessage) + private + pure + { + // Look for revert reason and bubble it up if present + if (returndata.length > 0) { + // The easiest way to bubble the revert reason is using memory via assembly + /// @solidity memory-safe-assembly + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } +} diff --git a/package-lock.json b/package-lock.json index a0514db..6a4dc32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "opera-sfc", + "name": "u2u-sfc", "version": "3.0.4-rc.3", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "opera-sfc", + "name": "u2u-sfc", "version": "3.0.4-rc.3", "license": "MIT", "dependencies": { From e071b0fb551a72f0010045c06bbe7e831f5a0c7e Mon Sep 17 00:00:00 2001 From: b1m0n Date: Wed, 10 Apr 2024 01:49:57 +0700 Subject: [PATCH 2/4] Update ECDSA lib version --- contracts/eip712/TestPaymaster.sol | 2 +- contracts/eip712/tmp/TestPaymasterBytecode | 2 +- contracts/libraries/ECDSA.sol | 25 +++------------------- 3 files changed, 5 insertions(+), 24 deletions(-) diff --git a/contracts/eip712/TestPaymaster.sol b/contracts/eip712/TestPaymaster.sol index ea15994..8a3f0ee 100644 --- a/contracts/eip712/TestPaymaster.sol +++ b/contracts/eip712/TestPaymaster.sol @@ -21,7 +21,7 @@ contract TestPaymaster is IPaymaster, EIP712 { } function signDataMessage(bytes32 suggestedSignedHash) internal view returns (bytes32) { - return ECDSA.toTypedDataHash(_domainSeparatorV4(), keccak256(abi.encode( + return _hashTypedDataV4(keccak256(abi.encode( signDataTypeHash(), suggestedSignedHash ))); diff --git a/contracts/eip712/tmp/TestPaymasterBytecode b/contracts/eip712/tmp/TestPaymasterBytecode index 457fa7a..c5c6ca3 100644 --- a/contracts/eip712/tmp/TestPaymasterBytecode +++ b/contracts/eip712/tmp/TestPaymasterBytecode @@ -1 +1 @@ -61014060405234801562000011575f80fd5b506040516200123b3803806200123b833981810160405281019062000037919062000314565b81815f828051906020012090505f828051906020012090505f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f90508260e08181525050816101008181525050620000946200013e60201b60201c565b60a08181525050620000ae8184846200014560201b60201c565b608081815250503073ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff16815250508061012081815250505050505050335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505062000469565b5f46905090565b5f838383620001596200013e60201b60201c565b30604051602001620001709594939291906200040e565b6040516020818303038152906040528051906020012090509392505050565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620001f082620001a8565b810181811067ffffffffffffffff82111715620002125762000211620001b8565b5b80604052505050565b5f620002266200018f565b9050620002348282620001e5565b919050565b5f67ffffffffffffffff821115620002565762000255620001b8565b5b6200026182620001a8565b9050602081019050919050565b5f5b838110156200028d57808201518184015260208101905062000270565b5f8484015250505050565b5f620002ae620002a88462000239565b6200021b565b905082815260208101848484011115620002cd57620002cc620001a4565b5b620002da8482856200026e565b509392505050565b5f82601f830112620002f957620002f8620001a0565b5b81516200030b84826020860162000298565b91505092915050565b5f80604083850312156200032d576200032c62000198565b5b5f83015167ffffffffffffffff8111156200034d576200034c6200019c565b5b6200035b85828601620002e2565b925050602083015167ffffffffffffffff8111156200037f576200037e6200019c565b5b6200038d85828601620002e2565b9150509250929050565b5f819050919050565b620003ab8162000397565b82525050565b5f819050919050565b620003c581620003b1565b82525050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f620003f682620003cb565b9050919050565b6200040881620003ea565b82525050565b5f60a082019050620004235f830188620003a0565b620004326020830187620003a0565b620004416040830186620003a0565b620004506060830185620003ba565b6200045f6080830184620003fd565b9695505050505050565b60805160a05160c05160e0516101005161012051610d8a620004b15f395f6102b901525f6102fb01525f6102da01525f61020801525f61025e01525f61028e0152610d8a5ff3fe608060405260043610610033575f3560e01c8063391c91d2146100375780638da5cb5b146100685780639f3cb0b014610092575b5f80fd5b610051600480360381019061004c9190610814565b6100ae565b60405161005f929190610964565b60405180910390f35b348015610073575f80fd5b5061007c610167565b60405161008991906109d1565b60405180910390f35b6100ac60048036038101906100a79190610a9d565b61018a565b005b5f60605f6100c46100be87610193565b866101db565b90505f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610153576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014a90610be3565b60405180910390fd5b63391c91d260e01b92505094509492505050565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b50505050505050565b5f6101d461019f610205565b6101a7610325565b846040516020016101b9929190610c10565b6040516020818303038152906040528051906020012061034c565b9050919050565b5f805f806101e9868661038c565b9250925092506101f982826103e1565b82935050505092915050565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614801561028757507f0000000000000000000000000000000000000000000000000000000000000000610285610543565b145b156102b4577f00000000000000000000000000000000000000000000000000000000000000009050610322565b61031f7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000061054a565b90505b90565b5f7f2bb156903cb7269fcabf8c90e4a20f3c4eaf1dde6b407dbc50a24ac21c9fab69905090565b5f6040517f190100000000000000000000000000000000000000000000000000000000000081528360028201528260228201526042812091505092915050565b5f805f60418451036103cc575f805f602087015192506040870151915060608701515f1a90506103be8882858561058a565b9550955095505050506103da565b5f600285515f1b9250925092505b9250925092565b5f60038111156103f4576103f3610c37565b5b82600381111561040757610406610c37565b5b031561053f576001600381111561042157610420610c37565b5b82600381111561043457610433610c37565b5b0361046b576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600381111561047f5761047e610c37565b5b82600381111561049257610491610c37565b5b036104d657805f1c6040517ffce698f70000000000000000000000000000000000000000000000000000000081526004016104cd9190610c73565b60405180910390fd5b6003808111156104e9576104e8610c37565b5b8260038111156104fc576104fb610c37565b5b0361053e57806040517fd78bce0c0000000000000000000000000000000000000000000000000000000081526004016105359190610c8c565b60405180910390fd5b5b5050565b5f46905090565b5f838383610556610543565b3060405160200161056b959493929190610ca5565b6040516020818303038152906040528051906020012090509392505050565b5f805f7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0845f1c11156105c6575f600385925092509250610667565b5f6001888888886040515f81526020016040526040516105e99493929190610d11565b6020604051602081039080840390855afa158015610609573d5f803e3d5ffd5b5050506020604051035190505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff160361065a575f60015f801b93509350935050610667565b805f805f1b935093509350505b9450945094915050565b5f604051905090565b5f80fd5b5f80fd5b5f819050919050565b61069481610682565b811461069e575f80fd5b50565b5f813590506106af8161068b565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b610703826106bd565b810181811067ffffffffffffffff82111715610722576107216106cd565b5b80604052505050565b5f610734610671565b905061074082826106fa565b919050565b5f67ffffffffffffffff82111561075f5761075e6106cd565b5b610768826106bd565b9050602081019050919050565b828183375f83830152505050565b5f61079561079084610745565b61072b565b9050828152602081018484840111156107b1576107b06106b9565b5b6107bc848285610775565b509392505050565b5f82601f8301126107d8576107d76106b5565b5b81356107e8848260208601610783565b91505092915050565b5f80fd5b5f610220828403121561080b5761080a6107f1565b5b81905092915050565b5f805f806080858703121561082c5761082b61067a565b5b5f610839878288016106a1565b945050602061084a878288016106a1565b935050604085013567ffffffffffffffff81111561086b5761086a61067e565b5b610877878288016107c4565b925050606085013567ffffffffffffffff8111156108985761089761067e565b5b6108a4878288016107f5565b91505092959194509250565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6108e4816108b0565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b83811015610921578082015181840152602081019050610906565b5f8484015250505050565b5f610936826108ea565b61094081856108f4565b9350610950818560208601610904565b610959816106bd565b840191505092915050565b5f6040820190506109775f8301856108db565b8181036020830152610989818461092c565b90509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6109bb82610992565b9050919050565b6109cb816109b1565b82525050565b5f6020820190506109e45f8301846109c2565b92915050565b5f80fd5b5f80fd5b5f8083601f840112610a0757610a066106b5565b5b8235905067ffffffffffffffff811115610a2457610a236109ea565b5b602083019150836001820283011115610a4057610a3f6109ee565b5b9250929050565b60028110610a53575f80fd5b50565b5f81359050610a6481610a47565b92915050565b5f819050919050565b610a7c81610a6a565b8114610a86575f80fd5b50565b5f81359050610a9781610a73565b92915050565b5f805f805f805f60c0888a031215610ab857610ab761067a565b5b5f88013567ffffffffffffffff811115610ad557610ad461067e565b5b610ae18a828b016109f2565b9750975050602088013567ffffffffffffffff811115610b0457610b0361067e565b5b610b108a828b016107f5565b9550506040610b218a828b016106a1565b9450506060610b328a828b016106a1565b9350506080610b438a828b01610a56565b92505060a0610b548a828b01610a89565b91505092959891949750929550565b5f82825260208201905092915050565b7f5061796d61737465723a20556e617574686f72697a6564207369676e617475725f8201527f6500000000000000000000000000000000000000000000000000000000000000602082015250565b5f610bcd602183610b63565b9150610bd882610b73565b604082019050919050565b5f6020820190508181035f830152610bfa81610bc1565b9050919050565b610c0a81610682565b82525050565b5f604082019050610c235f830185610c01565b610c306020830184610c01565b9392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b610c6d81610a6a565b82525050565b5f602082019050610c865f830184610c64565b92915050565b5f602082019050610c9f5f830184610c01565b92915050565b5f60a082019050610cb85f830188610c01565b610cc56020830187610c01565b610cd26040830186610c01565b610cdf6060830185610c64565b610cec60808301846109c2565b9695505050505050565b5f60ff82169050919050565b610d0b81610cf6565b82525050565b5f608082019050610d245f830187610c01565b610d316020830186610d02565b610d3e6040830185610c01565b610d4b6060830184610c01565b9594505050505056fea2646970667358221220d2a7d277607bf46c3ee79ab177bb58aabc4fccb2381b50fec4e37ec7e100e69a64736f6c63430008140033 \ No newline at end of file +61014060405234801562000011575f80fd5b50604051620012d5380380620012d5833981810160405281019062000037919062000314565b81815f828051906020012090505f828051906020012090505f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f90508260e08181525050816101008181525050620000946200013e60201b60201c565b60a08181525050620000ae8184846200014560201b60201c565b608081815250503073ffffffffffffffffffffffffffffffffffffffff1660c08173ffffffffffffffffffffffffffffffffffffffff16815250508061012081815250505050505050335f806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550505062000469565b5f46905090565b5f838383620001596200013e60201b60201c565b30604051602001620001709594939291906200040e565b6040516020818303038152906040528051906020012090509392505050565b5f604051905090565b5f80fd5b5f80fd5b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b620001f082620001a8565b810181811067ffffffffffffffff82111715620002125762000211620001b8565b5b80604052505050565b5f620002266200018f565b9050620002348282620001e5565b919050565b5f67ffffffffffffffff821115620002565762000255620001b8565b5b6200026182620001a8565b9050602081019050919050565b5f5b838110156200028d57808201518184015260208101905062000270565b5f8484015250505050565b5f620002ae620002a88462000239565b6200021b565b905082815260208101848484011115620002cd57620002cc620001a4565b5b620002da8482856200026e565b509392505050565b5f82601f830112620002f957620002f8620001a0565b5b81516200030b84826020860162000298565b91505092915050565b5f80604083850312156200032d576200032c62000198565b5b5f83015167ffffffffffffffff8111156200034d576200034c6200019c565b5b6200035b85828601620002e2565b925050602083015167ffffffffffffffff8111156200037f576200037e6200019c565b5b6200038d85828601620002e2565b9150509250929050565b5f819050919050565b620003ab8162000397565b82525050565b5f819050919050565b620003c581620003b1565b82525050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f620003f682620003cb565b9050919050565b6200040881620003ea565b82525050565b5f60a082019050620004235f830188620003a0565b620004326020830187620003a0565b620004416040830186620003a0565b620004506060830185620003ba565b6200045f6080830184620003fd565b9695505050505050565b60805160a05160c05160e0516101005161012051610e24620004b15f395f6104c701525f61050901525f6104e801525f61041601525f61046c01525f61049c0152610e245ff3fe608060405260043610610033575f3560e01c8063391c91d2146100375780638da5cb5b146100685780639f3cb0b014610092575b5f80fd5b610051600480360381019061004c9190610804565b6100ae565b60405161005f929190610954565b60405180910390f35b348015610073575f80fd5b5061007c610167565b60405161008991906109c1565b60405180910390f35b6100ac60048036038101906100a79190610a8d565b61018a565b005b5f60605f6100c46100be87610193565b866101d3565b90505f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610153576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161014a90610bd3565b60405180910390fd5b63391c91d260e01b92505094509492505050565b5f8054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b50505050505050565b5f6101cc61019f6101fd565b836040516020016101b1929190610c00565b60405160208183030381529060405280519060200120610224565b9050919050565b5f805f806101e1868661025c565b9250925092506101f182826102b1565b82935050505092915050565b5f7f2bb156903cb7269fcabf8c90e4a20f3c4eaf1dde6b407dbc50a24ac21c9fab69905090565b5f61022d610413565b8260405160200161023f929190610c9b565b604051602081830303815290604052805190602001209050919050565b5f805f604184510361029c575f805f602087015192506040870151915060608701515f1a905061028e88828585610533565b9550955095505050506102aa565b5f600285515f1b9250925092505b9250925092565b5f60038111156102c4576102c3610cd1565b5b8260038111156102d7576102d6610cd1565b5b031561040f57600160038111156102f1576102f0610cd1565b5b82600381111561030457610303610cd1565b5b0361033b576040517ff645eedf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002600381111561034f5761034e610cd1565b5b82600381111561036257610361610cd1565b5b036103a657805f1c6040517ffce698f700000000000000000000000000000000000000000000000000000000815260040161039d9190610d0d565b60405180910390fd5b6003808111156103b9576103b8610cd1565b5b8260038111156103cc576103cb610cd1565b5b0361040e57806040517fd78bce0c0000000000000000000000000000000000000000000000000000000081526004016104059190610d26565b60405180910390fd5b5b5050565b5f7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff163073ffffffffffffffffffffffffffffffffffffffff1614801561049557507f000000000000000000000000000000000000000000000000000000000000000061049361061a565b145b156104c2577f00000000000000000000000000000000000000000000000000000000000000009050610530565b61052d7f00000000000000000000000000000000000000000000000000000000000000007f00000000000000000000000000000000000000000000000000000000000000007f0000000000000000000000000000000000000000000000000000000000000000610621565b90505b90565b5f805f7f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0845f1c111561056f575f600385925092509250610610565b5f6001888888886040515f81526020016040526040516105929493929190610d5a565b6020604051602081039080840390855afa1580156105b2573d5f803e3d5ffd5b5050506020604051035190505f73ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1603610603575f60015f801b93509350935050610610565b805f805f1b935093509350505b9450945094915050565b5f46905090565b5f83838361062d61061a565b30604051602001610642959493929190610d9d565b6040516020818303038152906040528051906020012090509392505050565b5f604051905090565b5f80fd5b5f80fd5b5f819050919050565b61068481610672565b811461068e575f80fd5b50565b5f8135905061069f8161067b565b92915050565b5f80fd5b5f80fd5b5f601f19601f8301169050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6106f3826106ad565b810181811067ffffffffffffffff82111715610712576107116106bd565b5b80604052505050565b5f610724610661565b905061073082826106ea565b919050565b5f67ffffffffffffffff82111561074f5761074e6106bd565b5b610758826106ad565b9050602081019050919050565b828183375f83830152505050565b5f61078561078084610735565b61071b565b9050828152602081018484840111156107a1576107a06106a9565b5b6107ac848285610765565b509392505050565b5f82601f8301126107c8576107c76106a5565b5b81356107d8848260208601610773565b91505092915050565b5f80fd5b5f61022082840312156107fb576107fa6107e1565b5b81905092915050565b5f805f806080858703121561081c5761081b61066a565b5b5f61082987828801610691565b945050602061083a87828801610691565b935050604085013567ffffffffffffffff81111561085b5761085a61066e565b5b610867878288016107b4565b925050606085013567ffffffffffffffff8111156108885761088761066e565b5b610894878288016107e5565b91505092959194509250565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6108d4816108a0565b82525050565b5f81519050919050565b5f82825260208201905092915050565b5f5b838110156109115780820151818401526020810190506108f6565b5f8484015250505050565b5f610926826108da565b61093081856108e4565b93506109408185602086016108f4565b610949816106ad565b840191505092915050565b5f6040820190506109675f8301856108cb565b8181036020830152610979818461091c565b90509392505050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6109ab82610982565b9050919050565b6109bb816109a1565b82525050565b5f6020820190506109d45f8301846109b2565b92915050565b5f80fd5b5f80fd5b5f8083601f8401126109f7576109f66106a5565b5b8235905067ffffffffffffffff811115610a1457610a136109da565b5b602083019150836001820283011115610a3057610a2f6109de565b5b9250929050565b60028110610a43575f80fd5b50565b5f81359050610a5481610a37565b92915050565b5f819050919050565b610a6c81610a5a565b8114610a76575f80fd5b50565b5f81359050610a8781610a63565b92915050565b5f805f805f805f60c0888a031215610aa857610aa761066a565b5b5f88013567ffffffffffffffff811115610ac557610ac461066e565b5b610ad18a828b016109e2565b9750975050602088013567ffffffffffffffff811115610af457610af361066e565b5b610b008a828b016107e5565b9550506040610b118a828b01610691565b9450506060610b228a828b01610691565b9350506080610b338a828b01610a46565b92505060a0610b448a828b01610a79565b91505092959891949750929550565b5f82825260208201905092915050565b7f5061796d61737465723a20556e617574686f72697a6564207369676e617475725f8201527f6500000000000000000000000000000000000000000000000000000000000000602082015250565b5f610bbd602183610b53565b9150610bc882610b63565b604082019050919050565b5f6020820190508181035f830152610bea81610bb1565b9050919050565b610bfa81610672565b82525050565b5f604082019050610c135f830185610bf1565b610c206020830184610bf1565b9392505050565b5f81905092915050565b7f19010000000000000000000000000000000000000000000000000000000000005f82015250565b5f610c65600283610c27565b9150610c7082610c31565b600282019050919050565b5f819050919050565b610c95610c9082610672565b610c7b565b82525050565b5f610ca582610c59565b9150610cb18285610c84565b602082019150610cc18284610c84565b6020820191508190509392505050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b610d0781610a5a565b82525050565b5f602082019050610d205f830184610cfe565b92915050565b5f602082019050610d395f830184610bf1565b92915050565b5f60ff82169050919050565b610d5481610d3f565b82525050565b5f608082019050610d6d5f830187610bf1565b610d7a6020830186610d4b565b610d876040830185610bf1565b610d946060830184610bf1565b95945050505050565b5f60a082019050610db05f830188610bf1565b610dbd6020830187610bf1565b610dca6040830186610bf1565b610dd76060830185610cfe565b610de460808301846109b2565b969550505050505056fea2646970667358221220072ef40a10e6cee80466e8db32779bb3e99e9138e59300d541cb378055838be664736f6c63430008140033 \ No newline at end of file diff --git a/contracts/libraries/ECDSA.sol b/contracts/libraries/ECDSA.sol index 93f7380..c70a38e 100644 --- a/contracts/libraries/ECDSA.sol +++ b/contracts/libraries/ECDSA.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT +// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) -pragma solidity >=0.6.2; +pragma solidity ^0.8.20; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. @@ -94,7 +95,7 @@ library ECDSA { /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * - * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] + * See https://eips.ethereum.org/EIPS/eip-2098[ERC-2098 short signatures] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { @@ -170,24 +171,4 @@ library ECDSA { revert ECDSAInvalidSignatureS(errorArg); } } - - /** - * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). - * - * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with - * `\x19\x01` and hashing the result. It corresponds to the hash signed by the - * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. - * - * See {ECDSA-recover}. - */ - function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { - /// @solidity memory-safe-assembly - assembly { - let ptr := mload(0x40) - mstore(ptr, hex"19_01") - mstore(add(ptr, 0x02), domainSeparator) - mstore(add(ptr, 0x22), structHash) - digest := keccak256(ptr, 0x42) - } - } } \ No newline at end of file From 4486d9e597243606fb9c85d1650e42775c860f7e Mon Sep 17 00:00:00 2001 From: b1m0n Date: Wed, 10 Apr 2024 05:38:24 +0700 Subject: [PATCH 3/4] Change test paymaster contract --- contracts/eip712/TestPaymaster.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contracts/eip712/TestPaymaster.sol b/contracts/eip712/TestPaymaster.sol index 8a3f0ee..ddafdaa 100644 --- a/contracts/eip712/TestPaymaster.sol +++ b/contracts/eip712/TestPaymaster.sol @@ -33,8 +33,9 @@ contract TestPaymaster is IPaymaster, EIP712 { bytes memory _authenticationSignature, Transaction calldata _transaction ) external payable returns (bytes4 magic, bytes memory context) { - address recoverAddress = ECDSA.recover(signDataMessage(_suggestedSignedHash), _authenticationSignature); - require(recoverAddress == owner, "Paymaster: Unauthorized signature"); + if (_suggestedSignedHash == 0x0) { + return (0x00000000, context); + } return (PAYMASTER_VALIDATION_SUCCESS_MAGIC, context); } From 8b16a524c3a980eda1708b1779ae26061456c5c9 Mon Sep 17 00:00:00 2001 From: b1m0n Date: Sun, 14 Apr 2024 01:52:25 +0700 Subject: [PATCH 4/4] Change test paymaster contract --- contracts/eip712/TestPaymaster.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contracts/eip712/TestPaymaster.sol b/contracts/eip712/TestPaymaster.sol index ddafdaa..ebdf761 100644 --- a/contracts/eip712/TestPaymaster.sol +++ b/contracts/eip712/TestPaymaster.sol @@ -34,7 +34,9 @@ contract TestPaymaster is IPaymaster, EIP712 { Transaction calldata _transaction ) external payable returns (bytes4 magic, bytes memory context) { if (_suggestedSignedHash == 0x0) { - return (0x00000000, context); + return (0x00000001, context); + } else { + require(_suggestedSignedHash != bytes32(uint256(1)), "Paymaster: Test revert"); } return (PAYMASTER_VALIDATION_SUCCESS_MAGIC, context); }