Skip to content

Instantly share code, notes, and snippets.

@shobhitic
Last active May 1, 2023 23:18
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save shobhitic/9c281505002a50d95e55b4c261e3d477 to your computer and use it in GitHub Desktop.
Save shobhitic/9c281505002a50d95e55b4c261e3d477 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LazyMinting</title>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/ethers/5.6.9/ethers.umd.min.js"></script>
</head>
<body>
<script type="text/javascript">
const SIGNING_DOMAIN_NAME = "Voucher-Domain"
const SIGNING_DOMAIN_VERSION = "1"
const chainId = 1
const contractAddress = "0xa131AD247055FD2e2aA8b156A11bdEc81b9eAD95" // Put the address here from remix
const signer = new ethers.Wallet("503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb") // private key that I use for address 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
const domain = {
name: SIGNING_DOMAIN_NAME,
version: SIGNING_DOMAIN_VERSION,
verifyingContract: contractAddress,
chainId
}
async function createVoucher(tokenId, price, uri, buyer) {
const voucher = { tokenId, price, uri, buyer }
const types = {
LazyNFTVoucher: [
{name: "tokenId", type: "uint256"},
{name: "price", type: "uint256"},
{name: "uri", type: "string"},
{name: "buyer", type: "address"}
]
}
const signature = await signer._signTypedData(domain, types, voucher)
return {
...voucher,
signature
}
}
async function main() {
const voucher = await createVoucher(5, 50, "uri", "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4") // the address is the address which receives the NFT
console.log(`[${voucher.tokenId}, ${voucher.price}, "${voucher.uri}", "${voucher.buyer}", "${voucher.signature}"]`)
}
</script>
</body>
</html>
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts@4.7.3/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts@4.7.3/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts@4.7.3/access/Ownable.sol";
import "@openzeppelin/contracts@4.7.3/utils/cryptography/draft-EIP712.sol";
contract LazyNFT is ERC721, ERC721URIStorage, Ownable, EIP712 {
string private constant SIGNING_DOMAIN = "Voucher-Domain";
string private constant SIGNATURE_VERSION = "1";
address public minter;
constructor(address _minter) ERC721("LazyNFT", "LNFT") EIP712(SIGNING_DOMAIN, SIGNATURE_VERSION) {
minter = _minter;
}
struct LazyNFTVoucher {
uint256 tokenId;
uint256 price;
string uri;
address buyer;
bytes signature;
}
function recover(LazyNFTVoucher calldata voucher) public view returns (address) {
bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
keccak256("LazyNFTVoucher(uint256 tokenId,uint256 price,string uri,address buyer)"),
voucher.tokenId,
voucher.price,
keccak256(bytes(voucher.uri)),
voucher.buyer
)));
address signer = ECDSA.recover(digest, voucher.signature);
return signer;
}
function safeMint(LazyNFTVoucher calldata voucher)
public
payable
{
require(minter == recover(voucher), "Wrong signature.");
require(msg.value >= voucher.price, "Not enough ether sent.");
_safeMint(voucher.buyer, voucher.tokenId);
_setTokenURI(voucher.tokenId, voucher.uri);
}
// The following functions are overrides required by Solidity.
function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
super._burn(tokenId);
}
function tokenURI(uint256 tokenId)
public
view
override(ERC721, ERC721URIStorage)
returns (string memory)
{
return super.tokenURI(tokenId);
}
}
@damianlluch
Copy link

Hi, I found your code very useful.
I have a question for you.
Why do you use the minter variable? What's the point?

@shobhitic
Copy link
Author

@damianlluch so only allowed user can mint with the voucher (and not everyone). Also can't front run the transaction.

@novlio
Copy link

novlio commented May 1, 2023

I keep getting this error when i try and run the contract:

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
Returned error: {"jsonrpc":"2.0","error":"execution reverted","id":6426425325688160}

any thoughts?

@novlio
Copy link

novlio commented May 1, 2023

When i force the transaction i get:

Gas estimation errored with the following message (see below). The transaction execution will likely fail. Do you want to force sending?
Returned error: {"jsonrpc":"2.0","error":"err: max fee per gas less than block base fee: address 0x27fF22B4AFbF9b7b1de0D832c94142c9E0Edea22, maxFeePerGas: 154311703063 baseFee: 158976875280 (supplied gas 15025147)","id":6426425325688248}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment