Skip to content

Commit

Permalink
♻️ Make EIP712DomainSeparator Module-Friendly (#229)
Browse files Browse the repository at this point in the history
### 🕓 Changelog

This PR refactors the `EIP712DomainSeparator` contract to make it
module-friendly and ready for the breaking `0.4.0` release.

---------

Signed-off-by: Pascal Marco Caversaccio <pascal.caversaccio@hotmail.ch>
  • Loading branch information
pcaversaccio authored Apr 10, 2024
1 parent 40a74ab commit 8bd2eb1
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 46 deletions.
14 changes: 7 additions & 7 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,13 @@ ECDSATest:testRecoverWithTooShortSignature() (gas: 14545)
ECDSATest:testRecoverWithValidSignature() (gas: 21604)
ECDSATest:testRecoverWithWrongMessage() (gas: 21616)
ECDSATest:testRecoverWithWrongVersion() (gas: 15782)
EIP712DomainSeparatorTest:testCachedDomainSeparatorV4() (gas: 10440)
EIP712DomainSeparatorTest:testDomainSeparatorV4() (gas: 11715)
EIP712DomainSeparatorTest:testEIP712Domain() (gas: 18648)
EIP712DomainSeparatorTest:testFuzzDomainSeparatorV4(uint8) (runs: 256, μ: 11870, ~: 11890)
EIP712DomainSeparatorTest:testFuzzEIP712Domain(bytes1,uint8,bytes32,uint256[]) (runs: 256, μ: 22030, ~: 22034)
EIP712DomainSeparatorTest:testFuzzHashTypedDataV4(address,address,uint256,uint256,uint64) (runs: 256, μ: 10577, ~: 10577)
EIP712DomainSeparatorTest:testHashTypedDataV4() (gas: 13523)
EIP712DomainSeparatorTest:testCachedDomainSeparatorV4() (gas: 10476)
EIP712DomainSeparatorTest:testDomainSeparatorV4() (gas: 11731)
EIP712DomainSeparatorTest:testEIP712Domain() (gas: 18661)
EIP712DomainSeparatorTest:testFuzzDomainSeparatorV4(uint8) (runs: 256, μ: 11888, ~: 11906)
EIP712DomainSeparatorTest:testFuzzEIP712Domain(bytes1,uint8,bytes32,uint256[]) (runs: 256, μ: 22026, ~: 22027)
EIP712DomainSeparatorTest:testFuzzHashTypedDataV4(address,address,uint256,uint256,uint64) (runs: 256, μ: 10685, ~: 10685)
EIP712DomainSeparatorTest:testHashTypedDataV4() (gas: 13631)
ERC1155Invariants:invariantOwner() (runs: 256, calls: 3840, reverts: 3429)
ERC1155Test:testBalanceOfBatchCase1() (gas: 281522)
ERC1155Test:testBalanceOfBatchCase2() (gas: 236092)
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- [`ECDSA`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/src/snekmate/utils/ECDSA.vy): Make `ECDSA` module-friendly. ([#227](https://github.com/pcaversaccio/snekmate/pull/227))
- [`MessageHashUtils`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/src/snekmate/utils/MessageHashUtils.vy): Move the `ECDSA` message hash methods to a separate `MessageHashUtils` library module. ([#227](https://github.com/pcaversaccio/snekmate/pull/227))
- [`SignatureChecker`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/src/snekmate/utils/SignatureChecker.vy): Make `SignatureChecker` module-friendly. ([#228](https://github.com/pcaversaccio/snekmate/pull/228))
- [`EIP712DomainSeparator`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/src/snekmate/utils/EIP712DomainSeparator.vy): Make `EIP712DomainSeparator` module-friendly. ([#229](https://github.com/pcaversaccio/snekmate/pull/229))
- **Vyper Contract Deployer**
- [`VyperDeployer`](https://github.com/pcaversaccio/snekmate/blob/v0.1.0/lib/utils/VyperDeployer.sol): Improve error message in the event of a Vyper compilation error. ([#219](https://github.com/pcaversaccio/snekmate/pull/219))

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ src
├── Create2AddressMock — "Create2Address Module Reference Implementation"
├── ECDSAMock — "ECDSA Module Reference Implementation"
├── MessageHashUtilsMock — "MessageHashUtils Module Reference Implementation"
└── SignatureCheckerMock — "SignatureChecker Module Reference Implementation"
├── SignatureCheckerMock — "SignatureChecker Module Reference Implementation"
└── EIP712DomainSeparatorMock — "EIP712DomainSeparator Module Reference Implementation"
```

## 🎛 Installation
Expand Down
2 changes: 1 addition & 1 deletion src/snekmate/governance/TimelockController.vy
Original file line number Diff line number Diff line change
Expand Up @@ -774,7 +774,7 @@ def _execute(target: address, amount: uint256, payload: Bytes[1_024]):
success: bool = empty(bool)
success, return_data = raw_call(target, payload, max_outsize=255, value=amount, revert_on_failure=False)
if (not(success)):
if len(return_data) != empty(uint256):
if (len(return_data) != empty(uint256)):
# Bubble up the revert reason.
raw_revert(return_data)
else:
Expand Down
55 changes: 20 additions & 35 deletions src/snekmate/utils/EIP712DomainSeparator.vy
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ import interfaces.IERC5267 as IERC5267
implements: IERC5267


# @dev We import the `MessageHashUtils` module.
# @notice Please note that the `MessageHashUtils`
# module is stateless and therefore does not require
# the `uses` keyword for usage.
from . import MessageHashUtils as message_hash_utils


# @dev The 32-byte type hash for the EIP-712 domain separator.
_TYPE_HASH: constant(bytes32) = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")

Expand Down Expand Up @@ -81,31 +88,6 @@ def __init__(name_: String[50], version_: String[20]):
_CACHED_SELF = self


@external
@view
def domain_separator_v4() -> bytes32:
"""
@dev Returns the domain separator for the current chain.
@return bytes32 The 32-byte domain separator.
"""
return self._domain_separator_v4()


@external
@view
def hash_typed_data_v4(struct_hash: bytes32) -> bytes32:
"""
@dev Returns the hash of the fully encoded EIP-712
message for this domain.
@notice The definition of the hashed struct can be found here:
https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
@param struct_hash The 32-byte hashed struct.
@return bytes32 The 32-byte fully encoded EIP712
message hash for this domain.
"""
return self._to_typed_data_hash(self._domain_separator_v4(), struct_hash)


@external
@view
def eip712Domain() -> (bytes1, String[50], String[20], uint256, address, bytes32, DynArray[uint256, 128]):
Expand Down Expand Up @@ -136,14 +118,13 @@ def eip712Domain() -> (bytes1, String[50], String[20], uint256, address, bytes32
@view
def _domain_separator_v4() -> bytes32:
"""
@dev An `internal` helper function that returns the domain separator
for the current chain.
@dev Returns the domain separator for the current chain.
@return bytes32 The 32-byte domain separator.
"""
if (self == _CACHED_SELF and chain.id == _CACHED_CHAIN_ID):
return _CACHED_DOMAIN_SEPARATOR
else:
return self._build_domain_separator()

return self._build_domain_separator()


@internal
Expand All @@ -157,11 +138,15 @@ def _build_domain_separator() -> bytes32:


@internal
@pure
def _to_typed_data_hash(domain_separator: bytes32, struct_hash: bytes32) -> bytes32:
@view
def _hash_typed_data_v4(struct_hash: bytes32) -> bytes32:
"""
@dev Sourced from {ECDSA-to_typed_data_hash}.
@notice See {ECDSA-to_typed_data_hash} for the
function docstring.
@dev Returns the hash of the fully encoded EIP-712
message for this domain.
@notice The definition of the hashed struct can be found here:
https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
@param struct_hash The 32-byte hashed struct.
@return bytes32 The 32-byte fully encoded EIP712
message hash for this domain.
"""
return keccak256(concat(b"\x19\x01", domain_separator, struct_hash))
return message_hash_utils._to_typed_data_hash(self._domain_separator_v4(), struct_hash)
76 changes: 76 additions & 0 deletions src/snekmate/utils/mocks/EIP712DomainSeparatorMock.vy
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# pragma version ~=0.4.0b6
"""
@title EIP712DomainSeparator Module Reference Implementation
@custom:contract-name EIP712DomainSeparatorMock
@license GNU Affero General Public License v3.0 only
@author pcaversaccio
"""


# @dev We import and implement the `IERC5267` interface,
# which is written using standard Vyper syntax.
from ..interfaces import IERC5267
implements: IERC5267


# @dev We import and initialise the `EIP712DomainSeparator` module.
from .. import EIP712DomainSeparator as ed
initializes: ed


# @dev We export (i.e. the runtime bytecode exposes these
# functions externally, allowing them to be called using
# the ABI encoding specification) the `external` function
# `eip712Domain` from the `EIP712DomainSeparator` module.
# @notice Please note that you must always also export (if
# required by the contract logic) `public` declared `constant`,
# `immutable`, and state variables, for which Vyper automatically
# generates an `external` getter function for the variable.
exports: ed.eip712Domain


@deploy
@payable
def __init__(name_: String[50], version_: String[20]):
"""
@dev Initialises the domain separator and the parameter caches.
To omit the opcodes for checking the `msg.value` in the
creation-time EVM bytecode, the constructor is declared as
`payable`.
@notice The definition of the domain separator can be found here:
https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator.
Since the Vyper design requires strings of fixed size,
we arbitrarily set the maximum length for `name` to 50
characters and `version` to 20 characters.
@param name_ The maximum 50-character user-readable string name
of the signing domain, i.e. the name of the dApp or protocol.
@param version_ The maximum 20-character current main version of
the signing domain. Signatures from different versions are
not compatible.
"""
ed.__init__(name_, version_)


@external
@view
def domain_separator_v4() -> bytes32:
"""
@dev Returns the domain separator for the current chain.
@return bytes32 The 32-byte domain separator.
"""
return ed._domain_separator_v4()


@external
@view
def hash_typed_data_v4(struct_hash: bytes32) -> bytes32:
"""
@dev Returns the hash of the fully encoded EIP-712
message for this domain.
@notice The definition of the hashed struct can be found here:
https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
@param struct_hash The 32-byte hashed struct.
@return bytes32 The 32-byte fully encoded EIP712
message hash for this domain.
"""
return ed._hash_typed_data_v4(struct_hash)
4 changes: 2 additions & 2 deletions test/utils/EIP712DomainSeparator.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ contract EIP712DomainSeparatorTest is Test {
bytes memory args = abi.encode(_NAME, _VERSION);
EIP712domainSeparator = IEIP712DomainSeparator(
vyperDeployer.deployContract(
"src/snekmate/utils/",
"EIP712DomainSeparator",
"src/snekmate/utils/mocks/",
"EIP712DomainSeparatorMock",
args
)
);
Expand Down

0 comments on commit 8bd2eb1

Please sign in to comment.