// 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/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "./Valhalla.sol"; import "./BullRonToken.sol"; struct NFTDetail { uint price; } struct Profile { uint value; uint buy_reward; uint claim_at; } contract BullRunOld is Initializable, ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable, ERC721BurnableUpgradeable, OwnableUpgradeable { uint256 private _nextTokenId; uint8 public total_list; uint256 public total_coin; uint public global_pool; IUniswapV2Router02 public swapRouter; IERC20 public usdt; Valhalla public valhalla; BullRunToken public bull_token; address public reserve1; address public reserve2; uint[] private _bonusPercent; bool isClaimableProfit; bool isRankRewardClaimAble; mapping(uint => mapping(address => uint)) public nft_assets; mapping(address => mapping(address => address[])) public swap_paths; mapping(uint => NFTDetail) public nft_list; mapping(uint => address) public coin_list; mapping(address => Profile) public profile; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { _disableInitializers(); } function initialize( IUniswapV2Router02 _swapRouter, IERC20 _usdt, Valhalla _valhalla, address _reserve1, address _reserve2 ) public initializer { __ERC721_init("BullRun", "BLRN"); __ERC721Enumerable_init(); __ERC721URIStorage_init(); __ERC721Burnable_init(); __Ownable_init(); swapRouter = _swapRouter; usdt = _usdt; valhalla = _valhalla; reserve1 = _reserve1; reserve2 = _reserve2; _bonusPercent = [10, 7, 4, 3, 3, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1]; } function _baseURI() internal pure override returns (string memory) { return "https://www.globalnetwork.finance/erc721/"; } function safeMint(address to, string memory uri) private { uint256 tokenId = _nextTokenId++; _safeMint(to, tokenId); _setTokenURI(tokenId, uri); } function get_swap_path( address _addressIn, address _addressOut ) internal view returns (address[] memory) { address[] memory result = swap_paths[_addressIn][_addressOut]; if (result.length == 0) { address[] memory regular_path = new address[](2); regular_path[0] = _addressIn; regular_path[1] = _addressOut; return regular_path; } return result; } // The following functions are overrides required by Solidity. function tokenURI( uint256 tokenId ) public view override(ERC721Upgradeable, ERC721URIStorageUpgradeable) returns (string memory) { return super.tokenURI(tokenId); } 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) { uint currentValue = nft_list[firstTokenId].price; if (from == address(0)) { profile[to].value += currentValue; } else { profile[from].value -= currentValue; profile[to].value += currentValue; } super._beforeTokenTransfer(from, to, firstTokenId, batchSize); } function supportsInterface( bytes4 interfaceId ) public view override( ERC721Upgradeable, ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable ) returns (bool) { return super.supportsInterface(interfaceId); } 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 = (global_pool * 40) / 100; uint toRareUp = global_pool - 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 addCoin(address _cointAddres) public onlyOwner { uint current = total_coin; coin_list[current] = _cointAddres; total_coin++; } function addNft(uint _price) public onlyOwner { uint current = total_list; nft_list[current].price = _price; total_list++; } function startStopProfit() public onlyOwner { isClaimableProfit = !isClaimableProfit; } function setBullToken(BullRunToken _bullTokenAddress) public onlyOwner { require(bull_token != _bullTokenAddress, "address already set"); bull_token = _bullTokenAddress; } function swap( uint256 amountIn, address _addressIn, address _addressOut, address to ) private returns (uint[] memory amounts) { require( IERC20(_addressIn).approve(address(swapRouter), amountIn), "Approve failed." ); address[] memory path = get_swap_path(_addressIn, _addressOut); amounts = swapRouter.swapExactTokensForTokens( amountIn, 0, path, to, block.timestamp ); return amounts; } function _shareReward( address _user, uint _price ) private returns (uint, uint) { Rank rank; address referral; uint toGlobalPool = (_price * 20) / 100; uint toInvestment = (_price * 264) / 1000; // 26,4 percent uint toBullCoin = (_price * 136) / 1000; // 13.6 percent uint rest = _price; rest -= toInvestment; rest -= toBullCoin; rest -= toGlobalPool; global_pool += 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.buy_reward += bonus; rest -= bonus; } if (i > 2 && i < 8 && rank == Rank.Common) { _profile.buy_reward += bonus; rest -= bonus; } if (i < 3) { _profile.buy_reward += bonus; rest -= bonus; } // emit ShareToken(_user, referral, bonus, block.timestamp); (, , rank, referral, , , , ) = valhalla.accountMap(referral); } uint toReserve1 = rest / 2; profile[reserve1].buy_reward += toReserve1; rest -= toReserve1; profile[reserve2].buy_reward += rest; return (toBullCoin, toInvestment); } function buyNft(uint _listId) public onlyNetwork { uint price = nft_list[_listId].price; require(total_coin > 0, "coin not set yet"); require( usdt.transferFrom(msg.sender, address(this), price), "TransferFrom failed." ); require(usdt.approve(address(swapRouter), price), "Approve failed."); uint256 tokenId = _nextTokenId++; _safeMint(msg.sender, tokenId); _setTokenURI(tokenId, Strings.toString(_listId)); // usdt value after share to other network (uint usdt_to_bull_token, uint to_investment) = _shareReward( msg.sender, price ); bull_token.buyToken(usdt_to_bull_token); uint amountBull = bull_token.getValue(usdt_to_bull_token); nft_assets[tokenId][address(bull_token)] = amountBull; usdt.transfer(address(bull_token), usdt_to_bull_token); uint per_token_bought = to_investment / total_coin; for (uint i = 0; i < total_coin; i++) { uint[] memory amounts; amounts = swap( per_token_bought, address(usdt), coin_list[i], address(this) ); nft_assets[tokenId][coin_list[i]] = amounts[amounts.length - 1]; } } function claimBuyReward() public { uint getReward = profile[msg.sender].buy_reward; require(getReward > 0, "no reward left"); IERC20(usdt).transfer(msg.sender, getReward); profile[msg.sender].buy_reward = 0; } function claimRankReward() public isRankStarted { uint startAt = valhalla.rankRewardClaimableAt(); require( profile[msg.sender].claim_at < startAt, "You already claim Reward" ); uint myReward = getMyRankReward(msg.sender); Rank _myRank; (, , _myRank, , , , , ) = valhalla.accountMap(msg.sender); profile[msg.sender].claim_at = block.timestamp; require(_myRank != Rank.NoRank, "Boost Your Rank First"); if (_myRank == Rank.Common) { require( profile[msg.sender].value >= 1000 * 10 ** 6, "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 ** 6, "Require 3000 NFT Value To claim the Reward" ); usdt.transfer(msg.sender, myReward); } } function claimProfit(uint tokenId) public isStartProfit { require(ownerOf(tokenId) == msg.sender, "Not the owner"); uint bullTokenAmount = nft_assets[tokenId][address(bull_token)]; bull_token.transfer(msg.sender, bullTokenAmount); for (uint i = 0; i < total_coin; i++) { uint amount = nft_assets[tokenId][coin_list[i]]; if (amount > 0) { uint fee = (amount * 10) / 100; swap(amount - fee, coin_list[i], address(usdt), msg.sender); uint[] memory toReceiver = swap( fee, coin_list[i], address(usdt), address(this) ); uint usdtAmount = toReceiver[toReceiver.length - 1]; uint split = usdtAmount / 2; profile[reserve1].buy_reward = split; profile[reserve2].buy_reward = usdtAmount - split; delete nft_assets[tokenId][coin_list[i]]; } } _burn(tokenId); } modifier isStartProfit() { require(isClaimableProfit, "claim profit didn't start yet"); _; } modifier isRankStarted() { require(valhalla.isRankRewardClaimable(), "rank hasn't started"); _; } modifier onlyNetwork() { bool isReg; (isReg, , , , , , , ) = valhalla.accountMap(msg.sender); require(isReg, "Only Network Can buy Nft"); _; } function wdUSDT(uint amount) public onlyOwner { require(amount <= usdt.balanceOf(address(this)), "Insufficient USDT balance"); usdt.transfer(msg.sender, amount); } }