// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/StringsUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol"; import "./USDT.sol"; import "./lib/NFTGetter.sol"; import "./NFT.sol"; import "./Valhalla.sol"; contract NFTFounder is Initializable, ERC721Upgradeable, OwnableUpgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable, UUPSUpgradeable { using CountersUpgradeable for CountersUpgradeable.Counter; CountersUpgradeable.Counter private _tokenIdCounter; CountersUpgradeable.Counter private _cardIdCounter; USDT public usdtERC20; Valhalla valhalla; CardFounder public cardFounder; mapping(address => CardsRewardFounder) public nftFounder; uint public nftFounderPoolMarker; address public receiverAddress; uint256 public nftFounderPool; // events event BuyNFTs(address indexed _from, uint[] _tokenIds); event ClaimReward(address indexed _from, uint value); event DistributeRewards(address indexed from, uint value); /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize( USDT _usdtERC20, Valhalla _valhalla, address _receiver ) public initializer { __ERC721_init("NFTFounder", "NFTF"); __ERC721Enumerable_init(); __Ownable_init(); __UUPSUpgradeable_init(); valhalla = _valhalla; receiverAddress = _receiver; usdtERC20 = _usdtERC20; cardFounder.totalMinted = 0; _cardIdCounter.increment(); } modifier onlyValhalla() { require( msg.sender == address(valhalla), "Only Admin can perform action" ); _; } function setFounderProps(uint _price, uint _maxMinted) public { cardFounder.price = _price * 10 ** usdtERC20.decimals(); cardFounder.maxMinted = _maxMinted; } function myNftRewards( address _address ) public view returns (uint) { CardsRewardFounder memory nftCards = nftFounder[_address]; uint poolMarker = nftFounderPoolMarker; if (!(poolMarker > nftCards.currentRewards)) return 0; return (poolMarker - nftCards.currentRewards) * nftCards.ownedNfts; } function buyNFTs(uint amount) public { uint[] memory tokenIds = new uint[](amount); usdtERC20.transferFrom(msg.sender, receiverAddress, cardFounder.price * amount); require( cardFounder.maxMinted > cardFounder.totalMinted + amount, "Max minted reached" ); for (uint i = 0; i < amount; i++) { uint256 tokenId = _tokenIdCounter.current(); tokenIds[i] = tokenId; _tokenIdCounter.increment(); _safeMint(msg.sender, tokenId); } uint rewards = myNftRewards(msg.sender); if (rewards > 0) claimRewards(); CardsRewardFounder storage nftCards = nftFounder[msg.sender]; nftCards.ownedNfts += amount; cardFounder.totalMinted += amount; nftCards.currentRewards = nftFounderPoolMarker; emit BuyNFTs(msg.sender, tokenIds); } function claimRewards() public { uint rewards = myNftRewards(msg.sender); require(rewards > 0, "No rewards yet"); CardsRewardFounder storage nftCards = nftFounder[msg.sender]; nftCards.currentRewards = nftFounderPoolMarker; nftFounderPool -= rewards; usdtERC20.transfer(msg.sender, rewards); emit ClaimReward(msg.sender, rewards); } function distributeFounderRewards( uint value ) external onlyValhalla { nftFounderPoolMarker += value / cardFounder.totalMinted; nftFounderPool += value; } function safeMint(address to) public onlyOwner { uint256 tokenId = _tokenIdCounter.current(); _tokenIdCounter.increment(); _safeMint(to, tokenId); } function _authorizeUpgrade( address newImplementation ) internal override onlyOwner {} // The following functions are overrides required by Solidity. function _beforeTokenTransfer( address from, address to, uint256 tokenId, uint256 batchSize ) internal override(ERC721Upgradeable, ERC721EnumerableUpgradeable) { super._beforeTokenTransfer(from, to, tokenId, batchSize); } function _burn( uint256 tokenId ) internal override(ERC721Upgradeable, ERC721URIStorageUpgradeable) { super._burn(tokenId); } function tokenURI( uint256 tokenId ) public view override(ERC721Upgradeable, ERC721URIStorageUpgradeable) returns (string memory) { return super.tokenURI(tokenId); } function supportsInterface( bytes4 interfaceId ) public view override( ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable, ERC721Upgradeable ) returns (bool) { return super.supportsInterface(interfaceId); } function _baseURI() internal pure override returns (string memory) { return "https://globalnetwork.finance/api/image/founder/"; } function transferFrom( address from, address to, uint256 tokenId ) public override(ERC721Upgradeable, IERC721Upgradeable) { super.transferFrom(from, to, tokenId); CardsRewardFounder storage cardSender = nftFounder[from]; uint rewards = myNftRewards(from); if (rewards > 0) { cardSender.currentRewards = nftFounderPoolMarker; nftFounderPool -= rewards; usdtERC20.transfer(msg.sender, rewards); } require(cardSender.ownedNfts > 0, "no card left"); cardSender.ownedNfts -= 1; CardsRewardFounder storage cardReceiver = nftFounder[to]; cardReceiver.ownedNfts += 1; cardReceiver.currentRewards = nftFounderPoolMarker; } }