Hello, I am working on an erc-712 contract and including a presale mint function that is only successful if a signed message (that includes token recipient and tokenID) is passed as a parameter. The signed message comes from a signer account managed via the NFT website's server.
I would like to know if someone could somehow take a previous successful transaction and extract the signature to create a signed message that includes a different token recipient and different tokenID to successfully call the presale mint function?
Below is the current contract code for the presale mint function.
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol";
contract myNFT is ERC721Enumerable, EIP712, Ownable {
using ECDSA for bytes32;
using Strings for uint256;
struct PresaleVoucher {
uint256 tokenId;
address recipient;
bytes signature;
}
uint256 public constant PRESALE_MAX = 1000;
bool public presaleActive;
address private signerAddress;
string private constant SIGNING_DOMAIN = "MYNFT-Voucher";
string private constant SIGNATURE_VERSION = "1";
constructor()
ERC721("My NFT", "MYNFT")
EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION)
{
signerAddress = msg.sender; ///placeholder - will change for actual deployment
}
/**
* @notice activate presale
*/
function activatePresale() external onlyOwner {
!presaleActive ? presaleActive = true : presaleActive = false;
}
/**
* @notice mint presale
*/
function mintPresale(PresaleVoucher calldata _presaleVoucher) external {
require(presaleActive, "PRESALE_INACTIVE");
require(msg.sender == _presaleVoucher.recipient, "INVALID_RECIPIENT");
require(signerAddress == _verify(_presaleVoucher), "INVALID_SIGNER");
require(_presaleVoucher.tokenId <= PRESALE_MAX, "INVALID_TOKENID");
_safeMint(msg.sender, _presaleVoucher.tokenId);
}
/**
* @notice verify
*/
function _verify(PresaleVoucher calldata _presaleVoucher)
internal
view
returns (address)
{
bytes32 digest = _hash(_presaleVoucher);
return ECDSA.recover(digest, _presaleVoucher.signature);
}
/**
* @notice hash
*/
function _hash(PresaleVoucher calldata _presaleVoucher)
internal
view
returns (bytes32)
{
return
_hashTypedDataV4(
keccak256(
abi.encode(
keccak256(
"PresaleVoucher(uint256 tokenId,address recipient)"
),
_presaleVoucher.tokenId,
_presaleVoucher.recipient
)
)
);
}
}