// SPDX-License-Identifier: MIT pragma solidity ^0.8.17; import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "hardhat/console.sol"; import "./Valhalla.sol"; import "./USDT.sol"; struct Profile { uint value; uint buyReward; uint claimedAt; } struct NftDetail { uint price; string uri; } contract BullRun is Initializable, ERC721Upgradeable, OwnableUpgradeable, ERC721BurnableUpgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable { /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } uint256 _nextTokenId; address public reserve1; address public reserve2; address public nftBuy1; address public nftBuy2; uint public totalSales; uint public totalProfit; uint public profitCollected; uint public globalPool; bool public isClaimableProfit; uint[] _bonusPercent; mapping(uint => NftDetail) public listNft; mapping(uint => NftDetail) public tokenIdToListNft; mapping(address => Profile) public profile; USDT public usdt; Valhalla public valhalla; event ShareToken(address from, address to, uint bonusReward, uint rewardAt); event ClaimToken(address from, address to, uint bonusReward, uint rewardAt); function initialize( NftDetail[] memory _list, USDT _usdt, Valhalla _valhalla, address _reserve1, address _reserve2, address _nftBuy1, address _nftBuy2 ) public initializer { __ERC721_init("BullRun", "BLRN"); __ERC721URIStorage_init(); __ERC721Burnable_init(); __Ownable_init(); valhalla = _valhalla; usdt = _usdt; reserve1 = _reserve1; reserve2 = _reserve2; nftBuy1 = _nftBuy1; nftBuy2 = _nftBuy2; for (uint i; i < _list.length; i++) { listNft[i] = _list[i]; } _bonusPercent = [10, 7, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1]; } function safeMint( address to, string memory uri, NftDetail memory _nftDetail ) private { uint256 tokenId = _nextTokenId++; _safeMint(to, tokenId); _setTokenURI(tokenId, uri); tokenIdToListNft[tokenId] = _nftDetail; } // The following functions are overrides required by Solidity. function _baseURI() internal pure override returns (string memory) { return "https://www.globalnetwork.finance/api/erc1155/bull-run/"; } function tokenURI( uint256 tokenId ) public view override(ERC721URIStorageUpgradeable, ERC721Upgradeable) returns (string memory) { return super.tokenURI(tokenId); } function supportsInterface( bytes4 interfaceId ) public view override( ERC721URIStorageUpgradeable, ERC721Upgradeable, ERC721EnumerableUpgradeable ) returns (bool) { return super.supportsInterface(interfaceId); } function _burn( uint256 tokenId ) internal virtual override(ERC721URIStorageUpgradeable, ERC721Upgradeable) { super._burn(tokenId); } function _beforeTokenTransfer( address from, address to, uint256 firstTokenId, uint256 batchSize ) internal virtual override(ERC721Upgradeable, ERC721EnumerableUpgradeable) { // readjust value if value transfered NftDetail storage nft = tokenIdToListNft[firstTokenId]; profile[from].value -= nft.price; profile[to].value += nft.price; super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } function buyNft(uint _listId) public onlyNetwork { NftDetail storage nft = listNft[_listId]; safeMint(msg.sender, nft.uri, nft); totalSales += nft.price; usdt.transferFrom(msg.sender, address(this), nft.price); _shareReward(msg.sender, nft.price); } function _shareReward(address _user, uint _price) private { Rank rank; address referral; uint toInvestment = (_price * 40) / 100; uint toGlobalPool = (_price * 20) / 100; uint rest = _price; rest -= toInvestment; rest -= toGlobalPool; globalPool += toGlobalPool; (, , rank, referral, , , , ) = valhalla.accountMap(_user); for (uint i; i < _bonusPercent.length; i++) { if (referral == address(0)) break; uint bonus = (_price * _bonusPercent[i]) / 100; Profile storage _profile = profile[referral]; if (i > 7 && rank >= Rank.Rare) { _profile.buyReward += bonus; rest -= bonus; } if (i > 2 && i < 8 && rank == Rank.Common) { _profile.buyReward += bonus; rest -= bonus; } if (i < 3) { _profile.buyReward += bonus; rest -= bonus; } emit ShareToken(_user, referral, bonus, block.timestamp); (, , rank, referral, , , , ) = valhalla.accountMap(referral); } // throw out usdt usdt.transfer(nftBuy1, toInvestment / 2); usdt.transfer(nftBuy2, toInvestment / 2); uint toReserve1 = rest / 2; profile[reserve1].buyReward += toReserve1; rest -= toReserve1; profile[reserve2].buyReward += rest; } function claimBuyReward() public { require(profile[msg.sender].buyReward > 0, "no reward left"); usdt.transfer(msg.sender, profile[msg.sender].buyReward); profile[msg.sender].buyReward = 0; } function getMyRankReward(address _profile) public view returns (uint) { uint common; uint rare; uint superRare; uint epic; uint legend; uint superLegend; (common, rare, superRare, epic, legend, superLegend) = valhalla .rankDistribution(); Rank _myRank; (, , _myRank, , , , , ) = valhalla.accountMap(_profile); uint rareUp = rare + superRare + epic + legend + superLegend; uint toCommon = (globalPool * 40) / 100; uint toRareUp = globalPool - toCommon; if (_myRank == Rank.Common) { if (common == 0) return 0; uint commonGet = toCommon / common; return commonGet; } else if (_myRank != Rank.NoRank) { if (rareUp == 0) return 0; uint rareUpGet = toRareUp / rareUp; return rareUpGet; } else { return 0; } } function claimRankReward() public { bool isStart = valhalla.isRankRewardClaimable(); uint startAt = valhalla.rankRewardClaimableAt(); require(isStart, "rank hasn't started"); require( profile[msg.sender].claimedAt < startAt, "You already claim Reward" ); uint myReward = getMyRankReward(msg.sender); Rank _myRank; (, , _myRank, , , , , ) = valhalla.accountMap(msg.sender); profile[msg.sender].claimedAt = block.timestamp; require(_myRank != Rank.NoRank, "Boost Your Rank First"); if (_myRank == Rank.Common) { require( profile[msg.sender].value >= 1000 * 10 ** usdt.decimals(), "Require 1000 NFT Value To claim the Reward" ); usdt.transfer(msg.sender, myReward); } else if (_myRank != Rank.NoRank) { require( profile[msg.sender].value >= 1000 * 10 ** usdt.decimals(), "Require 3000 NFT Value To claim the Reward" ); usdt.transfer(msg.sender, myReward); } } function shareProfit(uint _profitAmount) public onlyOwner { totalProfit += _profitAmount; usdt.transferFrom(msg.sender, address(this), _profitAmount); } function startStop() public onlyOwner { isClaimableProfit = !isClaimableProfit; } function claimProfit(uint _tokenId) public isStartProfit { uint getProfit = totalProfit / totalSales; NftDetail storage nft = tokenIdToListNft[_tokenId]; usdt.transfer(msg.sender, getProfit * nft.price); nft.price = 0; nft.uri = ""; burn(_tokenId); } modifier isStartProfit() { require(isClaimableProfit, "claim profit didn't start yet"); _; } modifier onlyNetwork() { bool isReg; (isReg, , , , , , , ) = valhalla.accountMap(msg.sender); require(isReg, "Only Network Can buy Nft"); _; } } // di bagi 2 pool, // elit 40%; // rareUp 60%;