diff --git a/src/EIP712.sol b/src/EIP712.sol deleted file mode 100644 index 8021318..0000000 --- a/src/EIP712.sol +++ /dev/null @@ -1,80 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/// @title EIP-712 -/// -/// @notice Abstract EIP-712 implementation. -abstract contract EIP712 { - /// @notice Returns information about the `EIP712Domain` used to create EIP-712 compliant hashes. - /// - /// @dev Follows ERC-5267 (see https://eips.ethereum.org/EIPS/eip-5267). - /// - /// @return fields The bitmap of used fields. - /// @return name The value of the `EIP712Domain.name` field. - /// @return version The value of the `EIP712Domain.version` field. - /// @return chainId The value of the `EIP712Domain.chainId` field. - /// @return verifyingContract The value of the `EIP712Domain.verifyingContract` field. - /// @return salt The value of the `EIP712Domain.salt` field. - /// @return extensions The list of EIP numbers, that extends EIP-712 with new domain fields. - function eip712Domain() - external - view - virtual - returns ( - bytes1 fields, - string memory name, - string memory version, - uint256 chainId, - address verifyingContract, - bytes32 salt, - uint256[] memory extensions - ) - { - fields = hex"0f"; // `0b1111`. - (name, version) = _domainNameAndVersion(); - chainId = block.chainid; - verifyingContract = address(this); - salt = salt; // `bytes32(0)`. - extensions = extensions; // `new uint256[](0)`. - } - - /// @notice Returns the EIP-712 typed hash of the `CoinbaseSmartWalletMessage(bytes32 hash)` data structure. - /// - /// @dev Implements encode(domainSeparator : 𝔹²⁵⁶, message : 𝕊) = "\x19\x01" || domainSeparator || - /// hashStruct(message). - /// @dev See https://eips.ethereum.org/EIPS/eip-712#specification. - /// - /// @param messageHash The hash of message values. - //// - /// @return The resulting EIP-712 hash. - function _eip712Hash(bytes32 messageHash) internal view virtual returns (bytes32) { - return keccak256(abi.encodePacked("\x19\x01", _domainSeparator(), messageHash)); - } - - /// @notice Returns the `domainSeparator` used to create EIP-712 compliant hashes. - /// - /// @dev Implements domainSeparator = hashStruct(eip712Domain). - /// See https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator. - /// - /// @return The 32 bytes domain separator result. - function _domainSeparator() internal view returns (bytes32) { - (string memory name, string memory version) = _domainNameAndVersion(); - return keccak256( - abi.encode( - keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), - keccak256(bytes(name)), - keccak256(bytes(version)), - block.chainid, - address(this) - ) - ); - } - - /// @notice Returns the domain name and version to use when creating EIP-712 signatures. - /// - /// @dev MUST be defined by the implementation. - /// - /// @return name The user readable name of signing domain. - /// @return version The current major version of the signing domain. - function _domainNameAndVersion() internal view virtual returns (string memory name, string memory version); -} diff --git a/src/SpendPermissions.sol b/src/SpendPermissions.sol index b2f917c..570ffbd 100644 --- a/src/SpendPermissions.sol +++ b/src/SpendPermissions.sol @@ -5,8 +5,6 @@ import {IERC1271} from "openzeppelin-contracts/contracts/interfaces/IERC1271.sol import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import {CoinbaseSmartWallet} from "smart-wallet/CoinbaseSmartWallet.sol"; -import {EIP712} from "./EIP712.sol"; - /// @title SpendPermissions /// /// @notice Allow spending native and ERC20 tokens with a recurring allowance. @@ -14,7 +12,7 @@ import {EIP712} from "./EIP712.sol"; /// @dev Allowance and spend values capped at uint160 ~ 1e48. /// /// @author Coinbase (https://github.com/coinbase/smart-wallet-permissions) -contract SpendPermissions is EIP712 { +contract SpendPermissions { /// @notice A recurring allowance for an external spender to withdraw an account's tokens. struct RecurringAllowance { /// @dev Smart account this recurring allowance is valid for. @@ -43,11 +41,6 @@ contract SpendPermissions is EIP712 { uint160 spend; } - /// @notice Hash of EIP-712 message type - bytes32 internal constant _RECURRING_ALLOWANCE_TYPEHASH = keccak256( - "RecurringAllowance(address account,address spender,address token,uint48 start,uint48 end,uint48 period,uint160 allowance)" - ); - /// @notice ERC-7528 address convention for ether (https://eips.ethereum.org/EIPS/eip-7528). address public constant ETHER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; @@ -169,7 +162,10 @@ contract SpendPermissions is EIP712 { /// @param signature Signed hash of the recurring allowance data. function permit(RecurringAllowance memory recurringAllowance, bytes memory signature) public { // validate signature over recurring allowance data - if (_isValidSignature(recurringAllowance.account, getHash(recurringAllowance), signature)) { + if ( + IERC1271(recurringAllowance.account).isValidSignature(getHash(recurringAllowance), signature) + != IERC1271.isValidSignature.selector + ) { revert UnauthorizedRecurringAllowance(); } @@ -198,7 +194,7 @@ contract SpendPermissions is EIP712 { /// /// @return hash Hash of the recurring allowance and replay protection parameters. function getHash(RecurringAllowance memory recurringAllowance) public view returns (bytes32) { - return _eip712Hash(keccak256(abi.encode(_RECURRING_ALLOWANCE_TYPEHASH, recurringAllowance))); + return keccak256(abi.encode(recurringAllowance, block.chainid, address(this))); } /// @notice Return if recurring allowance is authorized i.e. approved and not revoked. @@ -332,23 +328,4 @@ contract SpendPermissions is EIP712 { function _execute(address account, address target, uint256 value, bytes memory data) internal virtual { CoinbaseSmartWallet(payable(account)).execute({target: target, value: value, data: data}); } - - /// @notice Validate an EIP-1271 contract signature. - /// - /// @param account Address of the user account. - /// @param hash Hash of the message. - /// @param signature Bytes for the user signature. - /// - /// @return isValid True if signature is valid. - function _isValidSignature(address account, bytes32 hash, bytes memory signature) internal view returns (bool) { - return IERC1271(account).isValidSignature(hash, signature) != IERC1271.isValidSignature.selector; - } - - /// @notice Returns the domain name and version to use when creating EIP-712 signatures. - /// - /// @return name The user readable name of signing domain. - /// @return version The current major version of the signing domain. - function _domainNameAndVersion() internal pure override returns (string memory name, string memory version) { - return ("SpendPermissions", "1"); - } }