Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save toddstephens335/8bca6e03d7db52bef093927c0d02964b to your computer and use it in GitHub Desktop.
Save toddstephens335/8bca6e03d7db52bef093927c0d02964b to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.8.19+commit.7dd6d404.js&optimize=true&runs=200&gist=
// SPDX-License-Identifier: MIT
// Copyright (c) 2021 the ethier authors (github.com/divergencetech/ethier)
pragma solidity >=0.8.0 <0.9.0;
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
/**
@title SignatureChecker
@notice Additional functions for EnumerableSet.Addresset that require a valid
ECDSA signature of a standardized message, signed by any member of the set.
*/
library SignatureChecker {
using EnumerableSet for EnumerableSet.AddressSet;
/**
@notice Requires that the message has not been used previously and that the
recovered signer is contained in the signers AddressSet.
@dev Convenience wrapper for message generation + signature verification
+ marking message as used
@param signers Set of addresses from which signatures are accepted.
@param usedMessages Set of already-used messages.
@param signature ECDSA signature of message.
*/
function requireValidSignature(
EnumerableSet.AddressSet storage signers,
bytes memory data,
bytes calldata signature,
mapping(bytes32 => bool) storage usedMessages
) internal {
bytes32 message = generateMessage(data);
require(
!usedMessages[message],
"SignatureChecker: Message already used"
);
usedMessages[message] = true;
requireValidSignature(signers, message, signature);
}
/**
@notice Requires that the message has not been used previously and that the
recovered signer is contained in the signers AddressSet.
@dev Convenience wrapper for message generation + signature verification.
*/
function requireValidSignature(
EnumerableSet.AddressSet storage signers,
bytes memory data,
bytes calldata signature
) internal view {
bytes32 message = generateMessage(data);
requireValidSignature(signers, message, signature);
}
/**
@notice Requires that the message has not been used previously and that the
recovered signer is contained in the signers AddressSet.
@dev Convenience wrapper for message generation from address +
signature verification.
*/
function requireValidSignature(
EnumerableSet.AddressSet storage signers,
address a,
bytes calldata signature
) internal view {
bytes32 message = generateMessage(abi.encodePacked(a));
requireValidSignature(signers, message, signature);
}
/**
@notice Common validator logic, checking if the recovered signer is
contained in the signers AddressSet.
*/
function validSignature(
EnumerableSet.AddressSet storage signers,
bytes32 message,
bytes calldata signature
) internal view returns (bool) {
return signers.contains(ECDSA.recover(message, signature));
}
/**
@notice Requires that the recovered signer is contained in the signers
AddressSet.
@dev Convenience wrapper that reverts if the signature validation fails.
*/
function requireValidSignature(
EnumerableSet.AddressSet storage signers,
bytes32 message,
bytes calldata signature
) internal view {
require(
validSignature(signers, message, signature),
"SignatureChecker: Invalid signature"
);
}
/**
@notice Generates a message for a given data input that will be signed
off-chain using ECDSA.
@dev For multiple data fields, a standard concatenation using
`abi.encodePacked` is commonly used to build data.
*/
function generateMessage(bytes memory data)
internal
pure
returns (bytes32)
{
return ECDSA.toEthSignedMessageHash(data);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment