Utilities
Multiple libraries and general purpose utilities included in the community version of OpenZeppelin Contracts. These are only a set of utility contracts. For the full list, check out the API Reference.
Cryptography
Validating Typed Data Signatures
For prior knowledge on how to validate signatures on-chain, check out the OpenZeppelin Contracts documentation
As opposed to validating plain-text messages, it is possible to let your users sign structured data (i.e. typed values) in a way that is still readable on their wallets. This is possible by implementing EIP712
, a standard way to encode structured data into a typed data hash.
To start validating signed typed structures, just validate the typed data hash:
// contracts/MyContractDomain.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {EIP712} from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
/// @dev Unsafe contract to demonstrate the use of EIP712 and ECDSA.
abstract contract MyContractDomain is EIP712 {
function validateSignature(
address mailTo,
string memory mailContents,
bytes memory signature
) internal view returns (address) {
bytes32 digest = _hashTypedDataV4(
keccak256(abi.encode(keccak256("Mail(address to,string contents)"), mailTo, keccak256(bytes(mailContents))))
);
return ECDSA.recover(digest, signature);
}
}
As part of the message, EIP-712 requires implementers to include a domain separator, which is a hash that includes the current smart contract address and the chain id where it’s deployed. This way, the smart contract can be sure that the structured message was signed for its specific domain, avoiding replayability of signatures in smart contracts.
Validating Nested EIP-712 Signatures
Accounts (i.e. Smart Contract Wallets or Smart Accounts) are particularly likely to be controlled by multiple signers. As such, it’s important to make sure that signatures are:
-
Only valid for the intended domain and account.
-
Validated in a way that’s readable for the end signer.
On one hand, making sure that the Account signature is only valid for an specific smart contract (i.e. an application) is difficult since it requires to validate a signature whose domain is the application but also the Account itself. For these reason, the community developed ERC-7739; a defensive rehashing mechanism that binds a signature to a single domain using a nested EIP-712 approach (i.e. an EIP-712 typed structure wrapping another).
In case your smart contract validates signatures, using ERC7739Signer
will implement the IERC1271
interface for validating smart contract signatures following the approach suggested by ERC-7739:
Unresolved include directive in modules/ROOT/pages/utilities.adoc - include::api:example$utils/cryptography/ERC7739SignerECDSA.sol[]