// SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; import '@openzeppelin/contracts/token/ERC1155/ERC1155.sol'; import '@openzeppelin/contracts/access/AccessControl.sol'; import '@openzeppelin/contracts/security/Pausable.sol'; import '@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol'; import '@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol'; import '@openzeppelin/contracts/utils/cryptography/draft-EIP712.sol'; import '@openzeppelin/contracts/utils/cryptography/ECDSA.sol'; contract LiquiditeamNft is ERC1155, AccessControl, Pausable, ERC1155Burnable, ERC1155Supply, EIP712 { bytes32 public constant URI_SETTER_ROLE = keccak256('URI_SETTER_ROLE'); bytes32 public constant CONTRACT_URI_SETTER_ROLE = keccak256('CONTRACT_URI_SETTER_ROLE'); bytes32 public constant PAUSER_ROLE = keccak256('PAUSER_ROLE'); bytes32 public constant MINTER_ROLE = keccak256('MINTER_ROLE'); bytes32 public constant TRANSFERRER_ROLE = keccak256('TRANSFERRER_ROLE'); string private _contractURI; mapping(uint256 => string) private _uris; mapping(uint256 => bool) private _usedPassIds; event TransferWithTransferPass(uint256 passId, address account, uint256 id, uint256 amount); constructor(address transferrer, string memory newContractURI) ERC1155('') EIP712('LiquiditeamNftTransferPass', '1') { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); _grantRole(URI_SETTER_ROLE, msg.sender); _grantRole(CONTRACT_URI_SETTER_ROLE, msg.sender); _grantRole(PAUSER_ROLE, msg.sender); _grantRole(MINTER_ROLE, msg.sender); _grantRole(TRANSFERRER_ROLE, transferrer); setContractURI(newContractURI); } function uri(uint256 id) public view virtual override returns (string memory) { return _uris[id]; } // obsolete function setURI(string memory newURI) public onlyRole(URI_SETTER_ROLE) { _setURI(newURI); } function contractURI() public view returns (string memory) { return _contractURI; } function setContractURI(string memory newURI) public onlyRole(CONTRACT_URI_SETTER_ROLE) { _contractURI = newURI; } function pause() public onlyRole(PAUSER_ROLE) { _pause(); } function unpause() public onlyRole(PAUSER_ROLE) { _unpause(); } function mint( address account, uint256 id, uint256 amount, bytes memory data ) public onlyRole(MINTER_ROLE) { _mint(account, id, amount, data); } function mintBatch( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public onlyRole(MINTER_ROLE) { _mintBatch(to, ids, amounts, data); } function transferWithTransferPass( bytes memory signature, uint256 passId, address account, uint256 id, uint256 amount, uint256 validUntil, string memory newUri ) public { bytes32 digest = _hashTypedDataV4( keccak256( abi.encode( keccak256( 'TransferPass(uint256 passId,address account,uint256 id,uint256 amount,uint256 validUntil,string newUri)' ), passId, account, id, amount, validUntil, keccak256(bytes(newUri)) ) ) ); address recoveredSigner = ECDSA.recover(digest, signature); require(hasRole(TRANSFERRER_ROLE, recoveredSigner), 'Only the transferrer sign transfer pass'); require(block.timestamp < validUntil, 'Transfer pass is expired'); require(!_usedPassIds[passId], 'Transfer pass was already used'); // no need to check if account == sender, because account is verified and token will be sent to account bytes memory uriBytes = bytes(_uris[id]); if (uriBytes.length == 0) { _uris[id] = newUri; } emit TransferWithTransferPass(passId, account, id, amount); _usedPassIds[passId] = true; _mint(account, id, amount, ''); } function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal override(ERC1155, ERC1155Supply) whenNotPaused { super._beforeTokenTransfer(operator, from, to, ids, amounts, data); } // The following functions are overrides required by Solidity. function supportsInterface(bytes4 interfaceId) public view override(ERC1155, AccessControl) returns (bool) { return super.supportsInterface(interfaceId); } }