Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: various informational issues #17

Merged
merged 3 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/ContractHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
pragma solidity 0.8.23;

/**
* @title A helper contract to get the address of a contract deployed by aan account with a given nonce.
* @title A helper contract to get the address of a contract deployed by an account with a given nonce.
* @dev See the following sources for very similar implementations:
* - https://github.com/foundry-rs/forge-std/blob/914702ae99c92fcc41db5128ae57d24a11be4a39/src/Script.sol
* - https://github.com/foundry-rs/forge-std/blob/578968243529db44acffcb802196ccab9b54db88/src/StdUtils.sol#L90
Expand Down
31 changes: 29 additions & 2 deletions src/ERC20Extended.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ pragma solidity 0.8.23;

import { IERC20 } from "./interfaces/IERC20.sol";
import { IERC20Extended } from "./interfaces/IERC20Extended.sol";
import { IERC5267 } from "./interfaces/IERC5267.sol";

import { ERC3009 } from "./ERC3009.sol";

/// @title An ERC20 token extended with EIP-2612 permits for signed approvals (via EIP-712 and with EIP-1271
/// compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
abstract contract ERC20Extended is IERC20Extended, ERC3009 {
/// and EIP-5267 compatibility), and extended with EIP-3009 transfer with authorization (via EIP-712).
abstract contract ERC20Extended is IERC20Extended, IERC5267, ERC3009 {
/**
* @inheritdoc IERC20Extended
* @dev Keeping this constant, despite `permit` parameter name differences, to ensure max EIP-2612 compatibility.
Expand Down Expand Up @@ -50,6 +51,32 @@ abstract contract ERC20Extended is IERC20Extended, ERC3009 {
return true;
}

/// @inheritdoc IERC5267
function eip712Domain()
external
view
virtual
returns (
bytes1 fields_,
string memory name_,
string memory version_,
uint256 chainId_,
address verifyingContract_,
bytes32 salt_,
uint256[] memory extensions_
)
{
return (
hex"0f", // 01111
_name,
"1",
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}

/// @inheritdoc IERC20Extended
function permit(
address owner_,
Expand Down
3 changes: 1 addition & 2 deletions src/ERC712.sol
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ abstract contract ERC712 is IERC712 {
* @param expiry_ Timestamp at which the signature expires or max uint256 for no expiry.
*/
function _revertIfExpired(uint256 expiry_) internal view {
if (expiry_ != type(uint256).max && block.timestamp > expiry_)
revert SignatureExpired(expiry_, block.timestamp);
if (block.timestamp > expiry_) revert SignatureExpired(expiry_, block.timestamp);
}

/**
Expand Down
47 changes: 47 additions & 0 deletions src/ERC712Extended.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import { IERC5267 } from "./interfaces/IERC5267.sol";

import { ERC712 } from "./ERC712.sol";

/// @title Typed structured data hashing and signing via EIP-712,
/// extended with EIP-5267 to describe and retrieve EIP-712 domain.
abstract contract ERC712Extended is ERC712, IERC5267 {
/**
* @notice Constructs ERC712Extended.
* @param name_ The name of the contract.
*/
constructor(string memory name_) ERC712(name_) {}

/******************************************************************************************************************\
| Public View/Pure Functions |
\******************************************************************************************************************/

/// @inheritdoc IERC5267
function eip712Domain()
external
view
virtual
returns (
bytes1 fields_,
string memory name_,
string memory version_,
uint256 chainId_,
address verifyingContract_,
bytes32 salt_,
uint256[] memory extensions_
)
{
return (
hex"0f", // 01111
_name,
"1",
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
}
23 changes: 23 additions & 0 deletions src/interfaces/IERC5267.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

/// @title Extension for EIP-712 to retrieve the EIP-712 domain.
interface IERC5267 {
/// @notice MAY be emitted to signal that the domain could have changed.
event EIP712DomainChanged();

/// @notice Returns the fields and values that describe the domain separator used by this contract for EIP-712.
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}
68 changes: 26 additions & 42 deletions src/libs/SignatureChecker.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,9 @@ library SignatureChecker {
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array signature.
* @return isValid Whether the signature is valid.
* @return Whether the signature is valid or not.
*/
function isValidSignature(
address signer,
bytes32 digest,
bytes memory signature
) internal view returns (bool isValid) {
function isValidSignature(address signer, bytes32 digest, bytes memory signature) internal view returns (bool) {
return isValidECDSASignature(signer, digest, signature) || isValidERC1271Signature(signer, digest, signature);
}

Expand All @@ -71,13 +67,13 @@ library SignatureChecker {
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ECDSA/secp256k1 signature (encoded r, s, v).
* @return isValid Whether the signature is valid.
* @return Whether the signature is valid or not.
*/
function isValidECDSASignature(
address signer,
bytes32 digest,
bytes memory signature
) internal pure returns (bool isValid) {
) internal pure returns (bool) {
if (signature.length == 64) {
(bytes32 r, bytes32 vs) = decodeShortECDSASignature(signature);
return isValidECDSASignature(signer, digest, r, vs);
Expand All @@ -92,14 +88,9 @@ library SignatureChecker {
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return isValid Whether the signature is valid.
* @return Whether the signature is valid or not.
*/
function isValidECDSASignature(
address signer,
bytes32 digest,
bytes32 r,
bytes32 vs
) internal pure returns (bool isValid) {
function isValidECDSASignature(address signer, bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (bool) {
return validateECDSASignature(signer, digest, r, vs) == Error.NoError;
}

Expand All @@ -110,15 +101,15 @@ library SignatureChecker {
* @param v An ECDSA/secp256k1 signature parameter.
* @param r An ECDSA/secp256k1 signature parameter.
* @param s An ECDSA/secp256k1 signature parameter.
* @return isValid Whether the signature is valid.
* @return Whether the signature is valid or not.
*/
function isValidECDSASignature(
address signer,
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (bool isValid) {
) internal pure returns (bool) {
return validateECDSASignature(signer, digest, v, r, s) == Error.NoError;
}

Expand All @@ -127,13 +118,13 @@ library SignatureChecker {
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ERC1271 signature.
* @return isValid Whether the signature is valid.
* @return Whether the signature is valid or not.
*/
function isValidERC1271Signature(
address signer,
bytes32 digest,
bytes memory signature
) internal view returns (bool isValid) {
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeCall(IERC1271.isValidSignature, (digest, signature))
);
Expand All @@ -148,13 +139,10 @@ library SignatureChecker {
* @dev Returns the signer of an ECDSA/secp256k1 signature for some digest.
* @param digest The hash of the data that was signed.
* @param signature A byte array ECDSA/secp256k1 signature.
* @return error An error, if any, that occurred during the signer recovery.
* @return signer The address of the account recovered form the signature (0 if error).
* @return An error, if any, that occurred during the signer recovery.
* @return The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(
bytes32 digest,
bytes memory signature
) internal pure returns (Error error, address signer) {
function recoverECDSASigner(bytes32 digest, bytes memory signature) internal pure returns (Error, address) {
if (signature.length != 65) return (Error.InvalidSignatureLength, address(0));

(uint8 v, bytes32 r, bytes32 s) = decodeECDSASignature(signature);
Expand All @@ -168,14 +156,10 @@ library SignatureChecker {
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return error An error, if any, that occurred during the signer recovery.
* @return signer The address of the account recovered form the signature (0 if error).
* @return An error, if any, that occurred during the signer recovery.
* @return The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(
bytes32 digest,
bytes32 r,
bytes32 vs
) internal pure returns (Error error, address signer) {
function recoverECDSASigner(bytes32 digest, bytes32 r, bytes32 vs) internal pure returns (Error, address) {
unchecked {
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
Expand All @@ -190,15 +174,15 @@ library SignatureChecker {
* @param v An ECDSA/secp256k1 signature parameter.
* @param r An ECDSA/secp256k1 signature parameter.
* @param s An ECDSA/secp256k1 signature parameter.
* @return error An error, if any, that occurred during the signer recovery.
* @return An error, if any, that occurred during the signer recovery.
* @return signer The address of the account recovered form the signature (0 if error).
*/
function recoverECDSASigner(
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (Error error, address signer) {
) internal pure returns (Error, address signer) {
// 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}.
if (uint256(s) > uint256(0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0))
Expand All @@ -216,13 +200,13 @@ library SignatureChecker {
* @param signer The address of the account purported to have signed.
* @param digest The hash of the data that was signed.
* @param signature A byte array ERC1271 signature.
* @return error An error, if any, that occurred during the signer recovery.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
bytes memory signature
) internal pure returns (Error error) {
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, signature);

return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
Expand All @@ -234,14 +218,14 @@ library SignatureChecker {
* @param digest The hash of the data that was signed.
* @param r An ECDSA/secp256k1 signature parameter.
* @param vs An ECDSA/secp256k1 short signature parameter.
* @return error An error, if any, that occurred during the signer recovery.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
bytes32 r,
bytes32 vs
) internal pure returns (Error error) {
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, r, vs);

return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
Expand All @@ -254,15 +238,15 @@ library SignatureChecker {
* @param v An ECDSA/secp256k1 signature parameter.
* @param r An ECDSA/secp256k1 signature parameter.
* @param s An ECDSA/secp256k1 signature parameter.
* @return error An error, if any, that occurred during the signer recovery.
* @return An error, if any, that occurred during the signer recovery.
*/
function validateECDSASignature(
address signer,
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (Error error) {
) internal pure returns (Error) {
(Error recoverError, address recoveredSigner) = recoverECDSASigner(digest, v, r, s);

return (recoverError == Error.NoError) ? validateRecoveredSigner(signer, recoveredSigner) : recoverError;
Expand All @@ -272,9 +256,9 @@ library SignatureChecker {
* @dev Returns an error if `signer` is not `recoveredSigner`.
* @param signer The address of the some signer.
* @param recoveredSigner The address of the some recoveredSigner.
* @return error An error if `signer` is not `recoveredSigner`.
* @return An error if `signer` is not `recoveredSigner`.
*/
function validateRecoveredSigner(address signer, address recoveredSigner) internal pure returns (Error error) {
function validateRecoveredSigner(address signer, address recoveredSigner) internal pure returns (Error) {
return (signer == recoveredSigner) ? Error.NoError : Error.SignerMismatch;
}
}
21 changes: 21 additions & 0 deletions test/ERC20Extended.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,27 @@ contract ERC20ExtendedTests is TestUtils {
assertEq(_token.decimals(), _TOKEN_DECIMALS);
}

/* ============ eip712Domain ============ */
function test_eip712Domain() public {
(
bytes1 fields_,
string memory name_,
string memory version_,
uint256 chainId_,
address verifyingContract_,
bytes32 salt_,
uint256[] memory extensions_
) = _token.eip712Domain();

assertEq(fields_, hex"0f");
assertEq(name_, _TOKEN_NAME);
assertEq(version_, "1");
assertEq(chainId_, block.chainid);
assertEq(verifyingContract_, address(_token));
assertEq(salt_, bytes32(0));
assertEq(extensions_, new uint256[](0));
}

/* ============ mint ============ */
function test_mint() public {
uint256 amount_ = 1e18;
Expand Down
3 changes: 0 additions & 3 deletions test/ERC712.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ pragma solidity 0.8.23;

import { TestUtils } from "./utils/TestUtils.t.sol";

import { SignatureChecker } from "../src/libs/SignatureChecker.sol";
import { SignatureCheckerHarness } from "./utils/SignatureCheckerHarness.sol";
import { ERC712Harness } from "./utils/ERC712Harness.sol";

import { IERC712 } from "../src/interfaces/IERC712.sol";
import { ERC712 } from "../src/ERC712.sol";

contract ERC712Tests is TestUtils {
ERC712Harness internal _erc712;
Expand Down
Loading
Loading