Skip to content

Instantly share code, notes, and snippets.

@ponyjackal
Last active September 14, 2022 03:46
Show Gist options
  • Save ponyjackal/5e607db31e7b219cdd2595dd90ffc9f1 to your computer and use it in GitHub Desktop.
Save ponyjackal/5e607db31e7b219cdd2595dd90ffc9f1 to your computer and use it in GitHub Desktop.
LazyMint using EIP712 - smart contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/common/ERC2981Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/utils/ERC721HolderUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/ECDSAUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/cryptography/draft-EIP712Upgradeable.sol";
import "erc721a-upgradeable/contracts/ERC721AUpgradeable.sol";
import "@tableland/evm/contracts/ITablelandTables.sol";
import "hardhat/console.sol";
contract LazyMint is
Initializable,
ReentrancyGuardUpgradeable,
OwnableUpgradeable,
PausableUpgradeable,
ERC721AUpgradeable,
ERC721HolderUpgradeable,
ERC2981Upgradeable,
EIP712Upgradeable
{
uint256 public constant TOTAL_SUPPLY = 15000;
uint256 public constant MINT_FEE = 0;
uint96 public constant ROYALTY_FEE = 1000;
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
string private constant SIGNING_DOMAIN = "LazyMintVoucher";
string private constant SIGNATURE_VERSION = "1";
struct LazyMintVoucher {
address receiver;
bytes16[] displayTypes;
bytes16[] traitTypes;
bytes16[] values;
bytes signature;
}
// -----------------------------------------
// LazyMint Events
// -----------------------------------------
event LazyMint(
address indexed to,
uint256 tokenId,
bytes16[] displayTypes,
bytes16[] indexed traitTypes,
bytes16[] values
);
// -----------------------------------------
// LazyMint Initializer
// -----------------------------------------
/**
* @dev Initializer function
* @param _baseURIString Base URI
* @param _description Description
* @param _image Image
* @param _externalURL External URL
* @param _royaltyReceiver Royalty receiver address
*/
function initialize(
string memory _baseURIString,
string memory _description,
string memory _image,
string memory _externalURL,
address payable _royaltyReceiver
) external initializerERC721A initializer {
__Context_init();
__Ownable_init();
__ReentrancyGuard_init();
__Pausable_init();
__ERC721A_init("Lazy Mint", "LMint");
__ERC721Holder_init();
__ERC2981_init();
__EIP712_init(SIGNING_DOMAIN, SIGNATURE_VERSION);
// Use ERC2981 to set royalty receiver and fee
_setDefaultRoyalty(_royaltyReceiver, ROYALTY_FEE);
}
// -----------------------------------------
// LazyMint Mutative Functions
// -----------------------------------------
/**
* @dev game server mints assets to the user
* @param _voucher The LazyMintVoucher
*/
function lazyMint(LazyMintVoucher calldata _voucher) external nonContract {
address signer = _verify(_voucher);
require(_voucher.traitTypes.length == _voucher.values.length, "LazyMint: invalid arguments");
require(signer == gameServer, "LazyMint: invalid signature");
require(msg.sender == _voucher.receiver, "LazyMint: invalid receiver");
_mint(_voucher.receiver, 1);
emit LazyMint(_voucher.receiver, tokenId, _voucher.displayTypes, _voucher.traitTypes, _voucher.values);
}
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721AUpgradeable, ERC2981Upgradeable)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
// -----------------------------------------
// LazyMint Internal Functions
// -----------------------------------------
function _bytes16ToString(bytes16 _bytes16) internal pure returns (string memory) {
uint8 i = 0;
while (i < 16 && _bytes16[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 16 && _bytes16[i] != 0; i++) {
bytesArray[i] = _bytes16[i];
}
return string(bytesArray);
}
/**
* @dev return a hash of the given LazyMintVoucher
*/
function _hash(LazyMintVoucher calldata _voucher) internal view returns (bytes32) {
return
_hashTypedDataV4(
keccak256(
abi.encode(
keccak256(
"LazyMintVoucher(address receiver,bytes16[] displayTypes,bytes16[] traitTypes,bytes16[] values)"
),
_voucher.receiver,
keccak256(abi.encodePacked(_voucher.displayTypes)),
keccak256(abi.encodePacked(_voucher.traitTypes)),
keccak256(abi.encodePacked(_voucher.values))
)
)
);
}
/**
* @dev verify the signature of a given LazyMintVoucher
* @param _voucher LazyMintVoucher
*/
function _verify(LazyMintVoucher calldata _voucher) internal view returns (address) {
bytes32 digest = _hash(_voucher);
return ECDSAUpgradeable.recover(digest, _voucher.signature);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment