Repository: pcaversaccio/snekmate (language: python)

Issue:
💥 Version `0x00` of EIP-191 in ECDSA Library Implementation
### Describe the desired feature:

### 🧐 Motivation
In the current `ECDSA` library, there is support to construct a message to sign according to the EIP191 Standard with the following:
- The version `0x45` (E) with `to_eth_signed_message_hash(..)` function 
- The version `0x01` with the `to_typed_data_hash(..)` function. 
But we are missing the `0x00` version.

<img width="1460" alt="Screen Shot 2022-10-19 at 10 17 27 PM" src="https://user-images.githubusercontent.com/86341666/196783740-8280e393-8449-4601-ab54-8a31e616531e.png">


### 📝 Details
Version`0x00` version is not that used because people are misusing the standard, they are using the `0x45` version for everything: signing messages for off-chain verification, for smart contract execution based on signatures, which is dangerous.

As people could be easily tricked into signing a normal message, thinking it is for login purposes, and then end up having execution based on this signature, so we should have a different mechanism, then different handling for execution based on signatures, and that's why we should make people use the `0x00` version for this case. + some projects are starting to use it like [xenium](https://github.com/Arachnid/xenium) and the [lsp-smart-contracts](https://github.com/lukso-network/lsp-smart-contracts/blob/v0.8.0/contracts/Custom/EIP191Signer.sol). 

I am suggesting adding a new function `to_data_with_intended_validator` or `to_data_with_intended_validator_hash` to be compatible with the version `0x00` taking 2 parameters, `<address validator>` and `<bytes dataToSign>`.

A library that supports the case: [EIP-191 Signer](https://github.com/lukso-network/tools-eip191-signer).
The Opened issue in OpenZeppelin: [Implement 0x00 version of EIP191 in ECDSA Library](https://github.com/OpenZeppelin/openzeppelin-contracts/issues/3772)

If you're okay with it I am happy to open the PR, otherwise, you can close the issue 😄  





### Code example that solves the feature:

```vy
@external
@pure
def to_data_with_intended_validator(validator: address, data_to_sign: Bytes[1024]=b"") -> bytes32:
    """
    @dev Returns an intended validator signed data according to 
    0x00 version of EIP-191.
    @param validator The address validating the signature
    @param struct_hash The data passed to be signed
    @return bytes32 The 32-byte intended validator signed data.
    """
    return keccak256(concat(b"\x19\x00", validator, data_to_sign))
```



Discussion / hints:
@YamenMerhi thanks for raising this point - actually I find it a reasonable addition to 🐍 snekmate. I would actually propose the following solution (ignoring the function docstrings):

```vyper
# @dev A Vyper contract cannot call directly between two `external` functions.
# To bypass this, we can use an interface.
interface IECDSA:
    def to_data_with_intended_validator_hash(validator: address, data: Bytes[1024]) -> bytes32: pure


@external
@view
def to_data_with_intended_validator_hash_self(data: Bytes[1024]) -> bytes32:
    return IECDSA(self).to_data_with_intended_validator_hash(self, data)


@external
@pure
def to_data_with_intended_validator_hash(validator: address, data: Bytes[1024]) -> bytes32:
    return keccak256(concat(b"\x19\x00", convert(validator, bytes20), data))
```

This includes two functions `to_data_with_intended_validator_hash_self` and `to_data_with_intended_validator_hash`, where one uses directly the contract address as validator `address`. If you agree with this, you can PR that, or we can discuss here about alternative solutions.

PS: I removed the default param `=b""` since otherwise, it can change the function selector, which I don't want. https://twitter.com/pcaversaccio/status/1621587151254163462
@pcaversaccio Your code makes sense, will do the PR. 👍 

Also, I don't mind the additional function, as in most of the cases of data with the intended validator, it will be used by some sort of a smart contract account that will validate the signature based on self. 

Sounds like a plan 👍

Fix patch:
diff --git a/.gas-snapshot b/.gas-snapshot
index 4db2460b..908eab3b 100644
--- a/.gas-snapshot
+++ b/.gas-snapshot
@@ -59,8 +59,8 @@ BatchDistributorTest:testDistributeTokenMultipleAddressesSuccess() (gas: 764652)
 BatchDistributorTest:testDistributeTokenOneAddressSuccess() (gas: 727265)
 BatchDistributorTest:testDistributeTokenRevertWithInsufficientAllowance() (gas: 721109)
 BatchDistributorTest:testDistributeTokenRevertWithInsufficientBalance() (gas: 723840)
-BatchDistributorTest:testFuzzDistributeEtherMultipleAddressesSuccess(((address,uint256)[]),uint256) (runs: 256, μ: 991885, ~: 925326)
-BatchDistributorTest:testFuzzDistributeTokenMultipleAddressesSuccess(((address,uint256)[]),address,uint256) (runs: 256, μ: 1465306, ~: 1430877)
+BatchDistributorTest:testFuzzDistributeEtherMultipleAddressesSuccess(((address,uint256)[]),uint256) (runs: 256, μ: 983113, ~: 924567)
+BatchDistributorTest:testFuzzDistributeTokenMultipleAddressesSuccess(((address,uint256)[]),address,uint256) (runs: 256, μ: 1465307, ~: 1430877)
 Create2AddressTest:testComputeAddress() (gas: 702836)
 Create2AddressTest:testComputeAddressSelf() (gas: 711578)
 Create2AddressTest:testFuzzComputeAddress(bytes32,address) (runs: 256, μ: 703369, ~: 703369)
@@ -88,11 +88,11 @@ CreateAddressTest:testComputeAddressSelfNonceUint8() (gas: 688618)
 CreateAddressTest:testComputeAddressSelfRevertTooHighNonce() (gas: 9122)
 CreateAddressTest:testFuzzComputeAddressNonce0x7f(uint64,address) (runs: 256, μ: 687670, ~: 687831)
 CreateAddressTest:testFuzzComputeAddressNonceUint16(uint64,address) (runs: 256, μ: 687167, ~: 687294)
-CreateAddressTest:testFuzzComputeAddressNonceUint24(uint64,address) (runs: 256, μ: 687296, ~: 687398)
+CreateAddressTest:testFuzzComputeAddressNonceUint24(uint64,address) (runs: 256, μ: 687297, ~: 687398)
 CreateAddressTest:testFuzzComputeAddressNonceUint32(uint64,address) (runs: 256, μ: 687294, ~: 687427)
 CreateAddressTest:testFuzzComputeAddressNonceUint40(uint64,address) (runs: 256, μ: 687401, ~: 687497)
 CreateAddressTest:testFuzzComputeAddressNonceUint48(uint64,address) (runs: 256, μ: 687399, ~: 687503)
-CreateAddressTest:testFuzzComputeAddressNonceUint56(uint64,address) (runs: 256, μ: 687401, ~: 687510)
+CreateAddressTest:testFuzzComputeAddressNonceUint56(uint64,address) (runs: 256, μ: 687403, ~: 687510)
 CreateAddressTest:testFuzzComputeAddressNonceUint64(uint64,address) (runs: 256, μ: 687431, ~: 687614)
 CreateAddressTest:testFuzzComputeAddressNonceUint8(uint64,address) (runs: 256, μ: 687169, ~: 687272)
 CreateAddressTest:testFuzzComputeAddressRevertTooHighNonce(uint256,address) (runs: 256, μ: 13321, ~: 13275)
@@ -106,27 +106,31 @@ CreateAddressTest:testFuzzComputeAddressSelfNonceUint56(uint64) (runs: 256, μ:
 CreateAddressTest:testFuzzComputeAddressSelfNonceUint64(uint64) (runs: 256, μ: 693746, ~: 693897)
 CreateAddressTest:testFuzzComputeAddressSelfNonceUint8(uint64) (runs: 256, μ: 693257, ~: 693334)
 CreateAddressTest:testFuzzComputeAddressSelfRevertTooHighNonce(uint256) (runs: 256, μ: 13522, ~: 13480)
-ECDSATest:testEthSignedMessageHash() (gas: 5847)
-ECDSATest:testFuzzEthSignedMessageHash(string) (runs: 256, μ: 6441, ~: 6443)
-ECDSATest:testFuzzRecoverWithInvalidSignature(bytes,string) (runs: 256, μ: 15174, ~: 15175)
-ECDSATest:testFuzzRecoverWithTooLongSignature(bytes,string) (runs: 256, μ: 13115, ~: 13113)
-ECDSATest:testFuzzRecoverWithValidSignature(string,string) (runs: 256, μ: 21703, ~: 21709)
-ECDSATest:testFuzzRecoverWithWrongMessage(string,string,bytes32) (runs: 256, μ: 21714, ~: 21724)
+ECDSATest:testEthSignedMessageHash() (gas: 5859)
+ECDSATest:testFuzzEthSignedMessageHash(string) (runs: 256, μ: 6452, ~: 6455)
+ECDSATest:testFuzzRecoverWithInvalidSignature(bytes,string) (runs: 256, μ: 15181, ~: 15182)
+ECDSATest:testFuzzRecoverWithTooLongSignature(bytes,string) (runs: 256, μ: 13144, ~: 13142)
+ECDSATest:testFuzzRecoverWithValidSignature(string,string) (runs: 256, μ: 21790, ~: 21796)
+ECDSATest:testFuzzRecoverWithWrongMessage(string,string,bytes32) (runs: 256, μ: 21778, ~: 21789)
+ECDSATest:testFuzzToDataWithIntendedValidatorHash(address,bytes) (runs: 256, μ: 7069, ~: 7057)
+ECDSATest:testFuzzToDataWithIntendedValidatorHashSelf(bytes) (runs: 256, μ: 10062, ~: 10045)
 ECDSATest:testFuzzToTypedDataHash(string,string) (runs: 256, μ: 7105, ~: 7108)
-ECDSATest:testRecoverWith0x00Value() (gas: 15565)
-ECDSATest:testRecoverWithArbitraryMessage() (gas: 20568)
-ECDSATest:testRecoverWithCorrectVersion() (gas: 21317)
-ECDSATest:testRecoverWithInvalidSignature() (gas: 15217)
-ECDSATest:testRecoverWithTooHighSValue() (gas: 15572)
-ECDSATest:testRecoverWithTooLongSignature() (gas: 12197)
-ECDSATest:testRecoverWithTooShortSignature() (gas: 13956)
-ECDSATest:testRecoverWithValidSignature() (gas: 20562)
-ECDSATest:testRecoverWithWrongMessage() (gas: 20624)
-ECDSATest:testRecoverWithWrongVersion() (gas: 15615)
-ECDSATest:testToTypedDataHash() (gas: 5905)
+ECDSATest:testRecoverWith0x00Value() (gas: 15623)
+ECDSATest:testRecoverWithArbitraryMessage() (gas: 20655)
+ECDSATest:testRecoverWithCorrectVersion() (gas: 21404)
+ECDSATest:testRecoverWithInvalidSignature() (gas: 15297)
+ECDSATest:testRecoverWithTooHighSValue() (gas: 15697)
+ECDSATest:testRecoverWithTooLongSignature() (gas: 12226)
+ECDSATest:testRecoverWithTooShortSignature() (gas: 13963)
+ECDSATest:testRecoverWithValidSignature() (gas: 20649)
+ECDSATest:testRecoverWithWrongMessage() (gas: 20711)
+ECDSATest:testRecoverWithWrongVersion() (gas: 15673)
+ECDSATest:testToDataWithIntendedValidatorHash() (gas: 11377)
+ECDSATest:testToDataWithIntendedValidatorHashSelf() (gas: 10220)
+ECDSATest:testToTypedDataHash() (gas: 5973)
 EIP712DomainSeparatorTest:testCachedDomainSeparatorV4() (gas: 7554)
 EIP712DomainSeparatorTest:testDomainSeparatorV4() (gas: 9332)
-EIP712DomainSeparatorTest:testFuzzDomainSeparatorV4(uint8) (runs: 256, μ: 9444, ~: 9473)
+EIP712DomainSeparatorTest:testFuzzDomainSeparatorV4(uint8) (runs: 256, μ: 9445, ~: 9473)
 EIP712DomainSeparatorTest:testFuzzHashTypedDataV4(address,address,uint256,uint256,uint64) (runs: 256, μ: 7971, ~: 7971)
 EIP712DomainSeparatorTest:testHashTypedDataV4() (gas: 13313)
 ERC1155Invariants:invariantOwner() (runs: 256, calls: 3840, reverts: 3479)
@@ -353,108 +357,108 @@ ERC20Test:testTransferToZeroAddress() (gas: 16812)
 ERC20Test:testTransferZeroTokens() (gas: 25327)
 ERC721Invariants:invariantOwner() (runs: 256, calls: 3840, reverts: 3449)
 ERC721Invariants:invariantTotalSupply() (runs: 256, calls: 3840, reverts: 3449)
-ERC721Test:testApproveClearingApprovalWithNoPriorApproval() (gas: 177983)
-ERC721Test:testApproveClearingApprovalWithPriorApproval() (gas: 187617)
-ERC721Test:testApproveFromApprovedAddress() (gas: 200709)
-ERC721Test:testApproveFromNonOwner() (gas: 173680)
-ERC721Test:testApproveFromOperatorAddress() (gas: 225161)
-ERC721Test:testApproveInvalidTokenId() (gas: 171657)
-ERC721Test:testApproveToOwner() (gas: 168051)
-ERC721Test:testApproveToZeroAddress() (gas: 187616)
-ERC721Test:testApproveWithNoPriorApproval() (gas: 197450)
-ERC721Test:testApproveWithPriorApprovalToDifferentAddress() (gas: 207111)
-ERC721Test:testApproveWithPriorApprovalToSameAddress() (gas: 205480)
-ERC721Test:testBalanceOfCase1() (gas: 328453)
+ERC721Test:testApproveClearingApprovalWithNoPriorApproval() (gas: 179010)
+ERC721Test:testApproveClearingApprovalWithPriorApproval() (gas: 188644)
+ERC721Test:testApproveFromApprovedAddress() (gas: 201736)
+ERC721Test:testApproveFromNonOwner() (gas: 174707)
+ERC721Test:testApproveFromOperatorAddress() (gas: 226188)
+ERC721Test:testApproveInvalidTokenId() (gas: 172684)
+ERC721Test:testApproveToOwner() (gas: 169078)
+ERC721Test:testApproveToZeroAddress() (gas: 188643)
+ERC721Test:testApproveWithNoPriorApproval() (gas: 198477)
+ERC721Test:testApproveWithPriorApprovalToDifferentAddress() (gas: 208138)
+ERC721Test:testApproveWithPriorApprovalToSameAddress() (gas: 206507)
+ERC721Test:testBalanceOfCase1() (gas: 330507)
 ERC721Test:testBalanceOfCase2() (gas: 11982)
 ERC721Test:testBalanceOfZeroAddress() (gas: 11088)
-ERC721Test:testBurnSuccess() (gas: 307446)
-ERC721Test:testBurnSuccessViaApproveAndSetApprovalForAll() (gas: 375144)
+ERC721Test:testBurnSuccess() (gas: 309089)
+ERC721Test:testBurnSuccessViaApproveAndSetApprovalForAll() (gas: 376788)
 ERC721Test:testCachedDomainSeparator() (gas: 8067)
 ERC721Test:testDomainSeparator() (gas: 11978)
-ERC721Test:testFuzzApproveClearingApprovalWithNoPriorApproval(address,address) (runs: 256, μ: 197468, ~: 197478)
-ERC721Test:testFuzzApproveClearingApprovalWithPriorApproval(address,address) (runs: 256, μ: 185644, ~: 185654)
-ERC721Test:testFuzzApproveFromNonOwner(address) (runs: 256, μ: 173066, ~: 173066)
-ERC721Test:testFuzzApproveFromOperatorAddress(address,address,address) (runs: 256, μ: 223802, ~: 223802)
-ERC721Test:testFuzzApproveWithNoPriorApproval(address,address) (runs: 256, μ: 197468, ~: 197478)
-ERC721Test:testFuzzApproveWithPriorApproval(address,address) (runs: 256, μ: 205501, ~: 205511)
-ERC721Test:testFuzzBurnSuccess(address) (runs: 256, μ: 306772, ~: 306764)
-ERC721Test:testFuzzDomainSeparator(uint8) (runs: 256, μ: 10071, ~: 10104)
-ERC721Test:testFuzzGetApprovedApprovedTokenId(address,address) (runs: 256, μ: 195028, ~: 195038)
-ERC721Test:testFuzzPermitInvalid(string,string,uint16) (runs: 256, μ: 203269, ~: 203305)
-ERC721Test:testFuzzPermitSuccess(string,string,uint16) (runs: 256, μ: 229794, ~: 229837)
+ERC721Test:testFuzzApproveClearingApprovalWithNoPriorApproval(address,address) (runs: 256, μ: 198495, ~: 198505)
+ERC721Test:testFuzzApproveClearingApprovalWithPriorApproval(address,address) (runs: 256, μ: 186671, ~: 186681)
+ERC721Test:testFuzzApproveFromNonOwner(address) (runs: 256, μ: 174093, ~: 174093)
+ERC721Test:testFuzzApproveFromOperatorAddress(address,address,address) (runs: 256, μ: 224829, ~: 224829)
+ERC721Test:testFuzzApproveWithNoPriorApproval(address,address) (runs: 256, μ: 198495, ~: 198505)
+ERC721Test:testFuzzApproveWithPriorApproval(address,address) (runs: 256, μ: 206528, ~: 206538)
+ERC721Test:testFuzzBurnSuccess(address) (runs: 256, μ: 308415, ~: 308407)
+ERC721Test:testFuzzDomainSeparator(uint8) (runs: 256, μ: 10076, ~: 10104)
+ERC721Test:testFuzzGetApprovedApprovedTokenId(address,address) (runs: 256, μ: 196055, ~: 196065)
+ERC721Test:testFuzzPermitInvalid(string,string,uint16) (runs: 256, μ: 204301, ~: 204332)
+ERC721Test:testFuzzPermitSuccess(string,string,uint16) (runs: 256, μ: 230826, ~: 230864)
 ERC721Test:testFuzzRenounceOwnershipNonOwner(address) (runs: 256, μ: 14408, ~: 14408)
 ERC721Test:testFuzzRenounceOwnershipSuccess(address) (runs: 256, μ: 49498, ~: 49482)
 ERC721Test:testFuzzSafeMintNonMinter(address) (runs: 256, μ: 16197, ~: 16197)
-ERC721Test:testFuzzSafeMintSuccess(address[]) (runs: 256, μ: 21171305, ~: 21116709)
-ERC721Test:testFuzzSafeTransferFromWithData(address,address,address,bytes) (runs: 256, μ: 1456566, ~: 1457269)
-ERC721Test:testFuzzSetApprovalForAllSuccess(address,address) (runs: 256, μ: 190192, ~: 190192)
-ERC721Test:testFuzzSetMinterNonOwner(address,string) (runs: 256, μ: 16190, ~: 16210)
+ERC721Test:testFuzzSafeMintSuccess(address[]) (runs: 256, μ: 21484809, ~: 21429329)
+ERC721Test:testFuzzSafeTransferFromWithData(address,address,address,bytes) (runs: 256, μ: 1458209, ~: 1458913)
+ERC721Test:testFuzzSetApprovalForAllSuccess(address,address) (runs: 256, μ: 191219, ~: 191219)
+ERC721Test:testFuzzSetMinterNonOwner(address,string) (runs: 256, μ: 16192, ~: 16210)
 ERC721Test:testFuzzSetMinterSuccess(string) (runs: 256, μ: 34775, ~: 34772)
-ERC721Test:testFuzzTokenByIndex(address,string[]) (runs: 256, μ: 24077371, ~: 24464364)
-ERC721Test:testFuzzTotalSupply(address,string[]) (runs: 256, μ: 23844114, ~: 24225068)
-ERC721Test:testFuzzTransferFrom(address,address,address) (runs: 256, μ: 570035, ~: 570019)
+ERC721Test:testFuzzTokenByIndex(address,string[]) (runs: 256, μ: 24213046, ~: 24635126)
+ERC721Test:testFuzzTotalSupply(address,string[]) (runs: 256, μ: 23979788, ~: 24395829)
+ERC721Test:testFuzzTransferFrom(address,address,address) (runs: 256, μ: 571678, ~: 571662)
 ERC721Test:testFuzzTransferOwnershipNonOwner(address,address) (runs: 256, μ: 14485, ~: 14485)
 ERC721Test:testFuzzTransferOwnershipSuccess(address,address) (runs: 256, μ: 77186, ~: 77167)
-ERC721Test:testGetApprovedApprovedTokenId() (gas: 194965)
+ERC721Test:testGetApprovedApprovedTokenId() (gas: 195992)
 ERC721Test:testGetApprovedInvalidTokenId() (gas: 11220)
-ERC721Test:testGetApprovedNotApprovedTokenId() (gas: 170476)
+ERC721Test:testGetApprovedNotApprovedTokenId() (gas: 171503)
 ERC721Test:testHasOwner() (gas: 10285)
-ERC721Test:testInitialSetup() (gas: 2452715)
-ERC721Test:testOwnerOf() (gas: 166028)
+ERC721Test:testInitialSetup() (gas: 2471464)
+ERC721Test:testOwnerOf() (gas: 167055)
 ERC721Test:testOwnerOfInvalidTokenId() (gas: 11130)
-ERC721Test:testPermitBadChainId() (gas: 201261)
-ERC721Test:testPermitBadNonce() (gas: 198344)
-ERC721Test:testPermitExpiredDeadline() (gas: 171222)
-ERC721Test:testPermitOtherSignature() (gas: 199716)
-ERC721Test:testPermitReplaySignature() (gas: 233227)
-ERC721Test:testPermitSuccess() (gas: 228734)
+ERC721Test:testPermitBadChainId() (gas: 202288)
+ERC721Test:testPermitBadNonce() (gas: 199371)
+ERC721Test:testPermitExpiredDeadline() (gas: 172249)
+ERC721Test:testPermitOtherSignature() (gas: 200743)
+ERC721Test:testPermitReplaySignature() (gas: 234254)
+ERC721Test:testPermitSuccess() (gas: 229761)
 ERC721Test:testRenounceOwnershipNonOwner() (gas: 11345)
 ERC721Test:testRenounceOwnershipSuccess() (gas: 23646)
 ERC721Test:testSafeMintNonMinter() (gas: 13591)
 ERC721Test:testSafeMintOverflow() (gas: 15814)
-ERC721Test:testSafeMintReceiverContract() (gas: 392834)
+ERC721Test:testSafeMintReceiverContract() (gas: 393861)
 ERC721Test:testSafeMintReceiverContractFunctionNotImplemented() (gas: 116912)
 ERC721Test:testSafeMintReceiverContractInvalidReturnIdentifier() (gas: 345758)
 ERC721Test:testSafeMintReceiverContractRevertsWithMessage() (gas: 343028)
 ERC721Test:testSafeMintReceiverContractRevertsWithPanic() (gas: 343253)
 ERC721Test:testSafeMintReceiverContractRevertsWithoutMessage() (gas: 342634)
-ERC721Test:testSafeMintSuccess() (gas: 519349)
+ERC721Test:testSafeMintSuccess() (gas: 529746)
 ERC721Test:testSafeMintToZeroAddress() (gas: 38767)
-ERC721Test:testSafeMintTokenAlreadyMinted() (gas: 360132)
-ERC721Test:testSafeTransferFromNoData() (gas: 1458870)
-ERC721Test:testSafeTransferFromReceiverFunctionNotImplemented() (gas: 183065)
-ERC721Test:testSafeTransferFromReceiverInvalidReturnIdentifier() (gas: 411906)
-ERC721Test:testSafeTransferFromReceiverRevertsWithMessage() (gas: 409221)
-ERC721Test:testSafeTransferFromReceiverRevertsWithPanic() (gas: 409491)
-ERC721Test:testSafeTransferFromReceiverRevertsWithoutMessage() (gas: 408783)
-ERC721Test:testSafeTransferFromWithData() (gas: 1221164)
-ERC721Test:testSetApprovalForAllOperatorIsOwner() (gas: 167581)
-ERC721Test:testSetApprovalForAllSuccessCase1() (gas: 196296)
-ERC721Test:testSetApprovalForAllSuccessCase2() (gas: 190149)
-ERC721Test:testSetApprovalForAllSuccessCase3() (gas: 203239)
+ERC721Test:testSafeMintTokenAlreadyMinted() (gas: 365299)
+ERC721Test:testSafeTransferFromNoData() (gas: 1460513)
+ERC721Test:testSafeTransferFromReceiverFunctionNotImplemented() (gas: 184092)
+ERC721Test:testSafeTransferFromReceiverInvalidReturnIdentifier() (gas: 412933)
+ERC721Test:testSafeTransferFromReceiverRevertsWithMessage() (gas: 410248)
+ERC721Test:testSafeTransferFromReceiverRevertsWithPanic() (gas: 410518)
+ERC721Test:testSafeTransferFromReceiverRevertsWithoutMessage() (gas: 409810)
+ERC721Test:testSafeTransferFromWithData() (gas: 1222808)
+ERC721Test:testSetApprovalForAllOperatorIsOwner() (gas: 168608)
+ERC721Test:testSetApprovalForAllSuccessCase1() (gas: 197323)
+ERC721Test:testSetApprovalForAllSuccessCase2() (gas: 191176)
+ERC721Test:testSetApprovalForAllSuccessCase3() (gas: 204266)
 ERC721Test:testSetMinterNonOwner() (gas: 13033)
 ERC721Test:testSetMinterRemoveOwnerAddress() (gas: 14220)
 ERC721Test:testSetMinterSuccess() (gas: 34584)
 ERC721Test:testSetMinterToZeroAddress() (gas: 16072)
 ERC721Test:testSupportsInterfaceGasCost() (gas: 5759)
-ERC721Test:testSupportsInterfaceInvalidInterfaceId() (gas: 5665)
-ERC721Test:testSupportsInterfaceSuccess() (gas: 9479)
-ERC721Test:testTokenByIndex() (gas: 559639)
-ERC721Test:testTokenByIndexOutOfBounds() (gas: 330858)
-ERC721Test:testTokenOfOwnerByIndex() (gas: 580824)
-ERC721Test:testTokenOfOwnerByIndexReverts() (gas: 553243)
-ERC721Test:testTokenURIAfterBurning() (gas: 139160)
-ERC721Test:testTokenURIDefault() (gas: 169125)
+ERC721Test:testSupportsInterfaceInvalidInterfaceId() (gas: 5688)
+ERC721Test:testSupportsInterfaceSuccess() (gas: 10447)
+ERC721Test:testTokenByIndex() (gas: 563747)
+ERC721Test:testTokenByIndexOutOfBounds() (gas: 332912)
+ERC721Test:testTokenOfOwnerByIndex() (gas: 584932)
+ERC721Test:testTokenOfOwnerByIndexReverts() (gas: 556324)
+ERC721Test:testTokenURIAfterBurning() (gas: 139981)
+ERC721Test:testTokenURIDefault() (gas: 170152)
 ERC721Test:testTokenURIInvalidTokenId() (gas: 11705)
-ERC721Test:testTokenURINoBaseURI() (gas: 2570496)
-ERC721Test:testTokenURINoTokenUri() (gas: 126199)
-ERC721Test:testTotalSupply() (gas: 328461)
-ERC721Test:testTransferFrom() (gas: 580916)
+ERC721Test:testTokenURINoBaseURI() (gas: 2590272)
+ERC721Test:testTokenURINoTokenUri() (gas: 127226)
+ERC721Test:testTotalSupply() (gas: 330515)
+ERC721Test:testTransferFrom() (gas: 582560)
 ERC721Test:testTransferOwnershipNonOwner() (gas: 13015)
 ERC721Test:testTransferOwnershipSuccess() (gas: 55029)
 ERC721Test:testTransferOwnershipToZeroAddress() (gas: 16064)
-MerkleProofVerificationTest:testFuzzMultiProofVerifySingleLeaf(bytes32[],uint256) (runs: 256, μ: 1649977829, ~: 1649974658)
-MerkleProofVerificationTest:testFuzzVerify(bytes32[],uint256) (runs: 256, μ: 135976484, ~: 135973284)
+MerkleProofVerificationTest:testFuzzMultiProofVerifySingleLeaf(bytes32[],uint256) (runs: 256, μ: 1649977830, ~: 1649974618)
+MerkleProofVerificationTest:testFuzzVerify(bytes32[],uint256) (runs: 256, μ: 135976485, ~: 135973244)
 MerkleProofVerificationTest:testFuzzVerifyMultiProofMultipleLeaves(bool,bool,bool) (runs: 256, μ: 412472500, ~: 412472432)
 MerkleProofVerificationTest:testInvalidMerkleMultiProof() (gas: 412478563)
 MerkleProofVerificationTest:testInvalidMerkleProof() (gas: 33970453)
@@ -507,7 +511,7 @@ OwnableTest:testTransferOwnershipNonOwner() (gas: 12551)
 OwnableTest:testTransferOwnershipSuccess() (gas: 22089)
 OwnableTest:testTransferOwnershipToZeroAddress() (gas: 15512)
 SignatureCheckerTest:testEIP1271NoIsValidSignatureFunction() (gas: 22865)
-SignatureCheckerTest:testEIP1271WithInvalidSignature(bytes,string) (runs: 256, μ: 23606, ~: 23598)
+SignatureCheckerTest:testEIP1271WithInvalidSignature(bytes,string) (runs: 256, μ: 23605, ~: 23598)
 SignatureCheckerTest:testEIP1271WithInvalidSignature1() (gas: 33857)
 SignatureCheckerTest:testEIP1271WithInvalidSignature2() (gas: 29023)
 SignatureCheckerTest:testEIP1271WithInvalidSigner() (gas: 33935)
@@ -518,8 +522,8 @@ SignatureCheckerTest:testEOAWithInvalidSignature2() (gas: 20267)
 SignatureCheckerTest:testEOAWithInvalidSigner() (gas: 20496)
 SignatureCheckerTest:testEOAWithTooHighSValue() (gas: 17256)
 SignatureCheckerTest:testEOAWithValidSignature() (gas: 19591)
-SignatureCheckerTest:testFuzzEIP1271WithInvalidSigner(string,string) (runs: 256, μ: 35896, ~: 35902)
+SignatureCheckerTest:testFuzzEIP1271WithInvalidSigner(string,string) (runs: 256, μ: 35895, ~: 35902)
 SignatureCheckerTest:testFuzzEIP1271WithValidSignature(string) (runs: 256, μ: 34429, ~: 34431)
 SignatureCheckerTest:testFuzzEOAWithInvalidSignature(bytes,string) (runs: 256, μ: 16901, ~: 16896)
-SignatureCheckerTest:testFuzzEOAWithInvalidSigner(string,string) (runs: 256, μ: 21577, ~: 21583)
-SignatureCheckerTest:testFuzzEOAWithValidSignature(string,string) (runs: 256, μ: 20688, ~: 20694)
\ No newline at end of file
+SignatureCheckerTest:testFuzzEOAWithInvalidSigner(string,string) (runs: 256, μ: 21576, ~: 21583)
+SignatureCheckerTest:testFuzzEOAWithValidSignature(string,string) (runs: 256, μ: 20687, ~: 20694)
\ No newline at end of file
diff --git a/lib/create-util b/lib/create-util
index 805c664c..e1ebc2e9 160000
--- a/lib/create-util
+++ b/lib/create-util
@@ -1,1 +1,1 @@
-Subproject commit 805c664c182fbf4b6588a008a67173d16a2ed53c
+Subproject commit e1ebc2e93df0e18b50a2cd3b7323bdc3373c5a8a
diff --git a/lib/forge-std b/lib/forge-std
index a2edd39d..5bbb2aa8 160000
--- a/lib/forge-std
+++ b/lib/forge-std
@@ -1,1 +1,1 @@
-Subproject commit a2edd39db95df7e9dd3f9ef9edc8c55fefddb6df
+Subproject commit 5bbb2aa82d86456dbf03305c3704cbd4fa93473f
diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts
index 96a2297e..adb861fb 160000
--- a/lib/openzeppelin-contracts
+++ b/lib/openzeppelin-contracts
@@ -1,1 +1,1 @@
-Subproject commit 96a2297e15f1a4bbcf470d2d0d6cb9c579c63893
+Subproject commit adb861fb3bb4b4167f10307b8d091326115502f9
diff --git a/src/utils/ECDSA.vy b/src/utils/ECDSA.vy
index ec30665c..d223bbcd 100644
--- a/src/utils/ECDSA.vy
+++ b/src/utils/ECDSA.vy
@@ -4,8 +4,10 @@
 @license GNU Affero General Public License v3.0
 @author pcaversaccio
 @notice These functions can be used to verify that a message was signed
-        by the holder of the private key of a given address. The implementation
-        is inspired by OpenZeppelin's implementation here:
+        by the holder of the private key of a given address. Additionally,
+        we provide helper functions to handle signed data in Ethereum
+        contracts based on EIP-191: https://eips.ethereum.org/EIPS/eip-191.
+        The implementation is inspired by OpenZeppelin's implementation here:
         https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/cryptography/ECDSA.sol.
 @custom:security Signatures must not be used as unique identifiers since the
                  `ecrecover` opcode allows for malleable (non-unique) signatures.
@@ -16,6 +18,12 @@ _MALLEABILITY_THRESHOLD: constant(bytes32) = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5
 _SIGNATURE_INCREMENT: constant(bytes32) = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
 
 
+# @dev A Vyper contract cannot call directly between two `external` functions.
+# To bypass this, we can use an interface.
+interface IECDSA:
+    def to_data_with_intended_validator_hash(validator: address, data: Bytes[1024]) -> bytes32: pure
+
+
 @external
 @payable
 def __init__():
@@ -65,6 +73,77 @@ def recover_sig(hash: bytes32, signature: Bytes[65]) -> address:
         return empty(address)
 
 
+@external
+@pure
+def to_eth_signed_message_hash(hash: bytes32) -> bytes32:
+    """
+    @dev Returns an Ethereum signed message from a 32-byte
+         message digest `hash`.
+    @notice This function returns a 32-byte hash that
+            corresponds to the one signed with the JSON-RPC method:
+            https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign.
+            This method is part of EIP-191:
+            https://eips.ethereum.org/EIPS/eip-191.
+    @param hash The 32-byte message digest.
+    @return bytes32 The 32-byte Ethereum signed message.
+    """
+    return keccak256(concat(b"\x19Ethereum Signed Message:\n32", hash))
+
+
+@external
+@pure
+def to_typed_data_hash(domain_separator: bytes32, struct_hash: bytes32) -> bytes32:
+    """
+    @dev Returns an Ethereum signed typed data from a 32-byte
+         `domain_separator` and a 32-byte `struct_hash`.
+    @notice This function returns a 32-byte hash that
+            corresponds to the one signed with the JSON-RPC method:
+            https://eips.ethereum.org/EIPS/eip-712#specification-of-the-eth_signtypeddata-json-rpc.
+            This method is part of EIP-712:
+            https://eips.ethereum.org/EIPS/eip-712.
+    @param domain_separator The 32-byte domain separator that is
+           used as part of the EIP-712 encoding scheme.
+    @param struct_hash The 32-byte struct hash that is used as
+           part of the EIP-712 encoding scheme. See the definition:
+           https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
+    @return bytes32 The 32-byte Ethereum signed typed data.
+    """
+    return keccak256(concat(b"\x19\x01", domain_separator, struct_hash))
+
+
+@external
+@view
+def to_data_with_intended_validator_hash_self(data: Bytes[1024]) -> bytes32:
+    """
+    @dev Returns an Ethereum signed data with this contract
+         as the intended validator and a maximum 1024-byte
+         payload `data`.
+    @notice This function structures the data according to
+            the version `0x00` of EIP-191:
+            https://eips.ethereum.org/EIPS/eip-191#version-0x00.
+    @param data The maximum 1024-byte data to be signed.
+    @return bytes32 The 32-byte Ethereum signed data.
+    """
+    return IECDSA(self).to_data_with_intended_validator_hash(self, data)
+
+
+@external
+@pure
+def to_data_with_intended_validator_hash(validator: address, data: Bytes[1024]) -> bytes32:
+    """
+    @dev Returns an Ethereum signed data with `validator` as
+         the intended validator and a maximum 1024-byte payload
+         `data`.
+    @notice This function structures the data according to
+            the version `0x00` of EIP-191:
+            https://eips.ethereum.org/EIPS/eip-191#version-0x00.
+    @param validator The 20-byte intended validator address.
+    @param data The maximum 1024-byte data to be signed.
+    @return bytes32 The 32-byte Ethereum signed data.
+    """
+    return keccak256(concat(b"\x19\x00", convert(validator, bytes20), data))
+
+
 @internal
 @pure
 def _recover_vrs(hash: bytes32, v: uint256, r: uint256, s: uint256) -> address:
@@ -126,41 +205,3 @@ def _try_recover_vrs(hash: bytes32, v: uint256, r: uint256, s: uint256) -> addre
         raise "ECDSA: invalid signature"
     
     return signer
-
-
-@external
-@pure
-def to_eth_signed_message_hash(hash: bytes32) -> bytes32:
-    """
-    @dev Returns an Ethereum signed message from a 32-byte
-         message digest `hash`.
-    @notice This function returns a 32-byte hash that
-            corresponds to the one signed with the JSON-RPC method:
-            https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_sign.
-            This method is part of EIP-191:
-            https://eips.ethereum.org/EIPS/eip-191.
-    @param hash The 32-byte message digest.
-    @return bytes32 The 32-byte Ethereum signed message.
-    """
-    return keccak256(concat(b"\x19Ethereum Signed Message:\n32", hash))
-
-
-@external
-@pure
-def to_typed_data_hash(domain_separator: bytes32, struct_hash: bytes32) -> bytes32:
-    """
-    @dev Returns an Ethereum signed typed data from a 32-byte
-         `domain_separator` and a 32-byte `struct_hash`.
-    @notice This function returns a 32-byte hash that
-            corresponds to the one signed with the JSON-RPC method:
-            https://eips.ethereum.org/EIPS/eip-712#specification-of-the-eth_signtypeddata-json-rpc.
-            This method is part of EIP-712:
-            https://eips.ethereum.org/EIPS/eip-712.
-    @param domain_separator The 32-byte domain separator that is
-           used as part of the EIP-712 encoding scheme.
-    @param struct_hash The 32-byte struct hash that is used as
-           part of the EIP-712 encoding scheme. See the definition:
-           https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct.
-    @return bytes32 The 32-byte Ethereum signed typed data.
-    """
-    return keccak256(concat(b"\x19\x01", domain_separator, struct_hash))


Test patch:
diff --git a/lib/prb-test b/lib/prb-test
index 2c767827..fa795366 160000
--- a/lib/prb-test
+++ b/lib/prb-test
@@ -1,1 +1,1 @@
-Subproject commit 2c7678271c450c88e76cf50d3b8a9314344a0475
+Subproject commit fa795366244b24c34896ac59f1b97d92dc022175
diff --git a/test/utils/ECDSA.t.sol b/test/utils/ECDSA.t.sol
index f7ab6f8b..d7ddaa7e 100644
--- a/test/utils/ECDSA.t.sol
+++ b/test/utils/ECDSA.t.sol
@@ -30,6 +30,8 @@ contract ECDSATest is Test {
 
     address private self = address(this);
     address private zeroAddress = address(0);
+    // solhint-disable-next-line var-name-mixedcase
+    address private ECDSAAddr;
 
     /**
      * @dev Transforms a standard signature into an EIP-2098
@@ -50,6 +52,7 @@ contract ECDSATest is Test {
 
     function setUp() public {
         ECDSA = IECDSA(vyperDeployer.deployContract("src/utils/", "ECDSA"));
+        ECDSAAddr = address(ECDSA);
     }
 
     function testRecoverWithValidSignature() public {
@@ -252,6 +255,28 @@ contract ECDSATest is Test {
         assertEq(digest1, digest2);
     }
 
+    function testToDataWithIntendedValidatorHash() public {
+        address validator = makeAddr("intendedValidator");
+        bytes memory data = new bytes(42);
+        bytes32 digest1 = ECDSA.to_data_with_intended_validator_hash(
+            validator,
+            data
+        );
+        bytes32 digest2 = keccak256(
+            abi.encodePacked("\x19\x00", validator, data)
+        );
+        assertEq(digest1, digest2);
+    }
+
+    function testToDataWithIntendedValidatorHashSelf() public {
+        bytes memory data = new bytes(42);
+        bytes32 digest1 = ECDSA.to_data_with_intended_validator_hash_self(data);
+        bytes32 digest2 = keccak256(
+            abi.encodePacked("\x19\x00", ECDSAAddr, data)
+        );
+        assertEq(digest1, digest2);
+    }
+
     function testFuzzRecoverWithValidSignature(
         string calldata signer,
         string calldata message
@@ -357,4 +382,28 @@ contract ECDSATest is Test {
         );
         assertEq(digest1, digest2);
     }
+
+    function testFuzzToDataWithIntendedValidatorHash(
+        address validator,
+        bytes calldata data
+    ) public {
+        bytes32 digest1 = ECDSA.to_data_with_intended_validator_hash(
+            validator,
+            data
+        );
+        bytes32 digest2 = keccak256(
+            abi.encodePacked("\x19\x00", validator, data)
+        );
+        assertEq(digest1, digest2);
+    }
+
+    function testFuzzToDataWithIntendedValidatorHashSelf(
+        bytes calldata data
+    ) public {
+        bytes32 digest1 = ECDSA.to_data_with_intended_validator_hash_self(data);
+        bytes32 digest2 = keccak256(
+            abi.encodePacked("\x19\x00", ECDSAAddr, data)
+        );
+        assertEq(digest1, digest2);
+    }
 }
diff --git a/test/utils/interfaces/IECDSA.sol b/test/utils/interfaces/IECDSA.sol
index eff5e094..641593a2 100644
--- a/test/utils/interfaces/IECDSA.sol
+++ b/test/utils/interfaces/IECDSA.sol
@@ -15,4 +15,13 @@ interface IECDSA {
         bytes32 domainSeparator,
         bytes32 structHash
     ) external pure returns (bytes32);
+
+    function to_data_with_intended_validator_hash_self(
+        bytes calldata data
+    ) external view returns (bytes32);
+
+    function to_data_with_intended_validator_hash(
+        address validator,
+        bytes calldata data
+    ) external pure returns (bytes32);
 }