// SPDX-License-Identifier: MIT pragma solidity 0.8.10; import "@openzeppelin/contracts/access/AccessControlEnumerable.sol"; import "../interfaces/IBEERC721.sol"; contract NftDistributor is AccessControlEnumerable { bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE"); bytes32 public constant MANAGE_ROLE = keccak256("MANAGE_ROLE"); // user address => nft ids mapping(address => uint256[]) public ownerToNFTs; // nft id => minted mapping(uint256 => bool) public nftMinted; IBEERC721 public nft; address public source; event Minted( address indexed user, address indexed nft, uint256[] nftSIds, uint256[] nftTIds ); /** * @dev Contract constructor. * * Initializes the contract with the specified addresses and sets the admin and management roles. * * Parameters: * - _nftTarget: The address of NFT that will be minted by this contract. Need MINTER_ROLE * - _nftSource: The address of the source NFT that will check if user has mint permission. * - _manageAddress: The address that will have the MANAGE_ROLE assigned. */ constructor(address _nftTarget, address _nftSource, address _manageAddress) { // Set up the ADMIN_ROLE and MANAGE_ROLE _setRoleAdmin(ADMIN_ROLE, ADMIN_ROLE); _setRoleAdmin(MANAGE_ROLE, ADMIN_ROLE); // Grant the ADMIN_ROLE to the deployer and to the contract itself _setupRole(ADMIN_ROLE, _msgSender()); _setupRole(ADMIN_ROLE, address(this)); // Grant the MANAGE_ROLE to the specified address _setupRole(MANAGE_ROLE, _manageAddress); // Initialize the nft contract with IBEERC721 interface nft = IBEERC721(_nftTarget); // Set the source contract address source = _nftSource; } /** * @dev Modifier that checks if the caller has the MANAGE_ROLE. * * The function uses the _checkRole() function from the AccessControl library. * If the check is successful, the code defined by the function that uses this modifier is executed. */ modifier onlyManager() { // Check if the caller has the MANAGE_ROLE _checkRole(MANAGE_ROLE, _msgSender()); // If the check is successful, execute the code in the function that uses this modifier _; } /** * @dev The mintToUser function mints NFTs to a given user. * * Only functions called by an address with the MANAGE_ROLE permission can access this function. * The function takes an array of nftIds and checks that there are enough mintable NFTs for the user. * It then loops through the user's owned NFTs, checking if each is mintable, and if so, adds it to a list to be minted. * Then it emits the Minted event with details of the operation. * * @param _user - The address of the user receiving the NFTs * @param _nftIds - An array of the IDs for the NFTs being minted */ function mintToUser( address _user, uint256[] calldata _nftIds ) external onlyManager { // Check that there are enough mintable NFTs require( _nftIds.length <= getMintableCount(_user), "Mintable count is not enough" ); // Get the array of the user's owned NFTs uint256[] memory nfts = ownerToNFTs[_user]; // Initialize variables uint256 index = 0; uint256[] memory nftSource = new uint256[](_nftIds.length); // Loop through the user's owned NFTs for (uint256 i = 0; i < nfts.length; i++) { uint256 nftSId = nfts[i]; // Check if the NFT is mintable if (!nftMinted[nftSId]) { // If the NFT is mintable, add it to the list to be minted uint256 nftId = _nftIds[index]; // Add the NFT's source ID to the list of sources nftSource[index] = nftSId; nftMinted[nftSId] = true; // Mint the NFT to the user's address nft.mint(_user, nftId); index++; } } // Emit event with details of the minting operation emit Minted(_user, address(nft), nftSource, _nftIds); } /** * @dev The addNFTData function adds an array of NFT IDs to the list of NFT IDs owned by a user. * mintToUser method would check if target user had permission to mint NFTs. * * Only functions called by an address with the MANAGE_ROLE permission can access this function. * The function takes in the address of the user and an array of NFT IDs. It then loops through the array * of NFT IDs, and adds each one to the end of the array of NFTs owned by that user to update their ownership data. * * @param _user - The address of the user being updated with new NFT data * @param _nftIds - An array of NFT IDs being added to the user's NFT data */ function addNFTData( address _user, uint256[] calldata _nftIds ) external onlyManager { // Loop through the array of NFT IDs for (uint256 i = 0; i < _nftIds.length; i++) { // Add each NFT ID to the end of the array of NFTs owned by the user ownerToNFTs[_user].push(_nftIds[i]); } } /** * @dev The getMintableCount function gets the number of NFTs owned by a user that have not yet been minted. * * This is a read-only function, meaning it doesn't modify the state of the blockchain. * It takes in the address of the user whose mintable count is being determined, * and returns the number of NFTs owned by the user that have not yet been minted. * * @param _user - The address of the user whose mintable count is being determined * @return count - The number of NFTs owned by the user that have not yet been minted */ function getMintableCount(address _user) public view returns (uint256) { // Get an array of all NFT IDs owned by the user uint256[] memory nfts = ownerToNFTs[_user]; // Initialize count to zero uint256 count = 0; // Loop through the array of NFT IDs for (uint256 i = 0; i < nfts.length; i++) { // Get the NFT ID at this index of the loop uint256 nftAId = nfts[i]; // Check if the NFT has not yet been minted if (!nftMinted[nftAId]) { // If the NFT has not yet been minted, increment the mintable count count++; } } // Return the final mintable count return count; } }