// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "./NFT.sol"; import "./Pool.sol"; import "./PledgeType.sol"; import "./Utils.sol"; contract Pledge is Initializable { NFT nftContract; Pool poolContract; ProductInfo[] public productInfo; mapping(uint256 => PledgeType) pledges; mapping(address => PledgeType[]) public pledgeRecords; mapping(uint256 => PledgeType) public invitationPledges; mapping(address => RecommendObjType) public recommendObj; mapping(address => uint256[]) public invitationTokens; mapping(address => address[]) public invitationAddress; mapping(address => PledgeWithdrawRecordType[]) public pledgeWithdrawRecord; mapping(address => InvitationWithdrawRecordType[]) public invitationWithdrawRecord; address[] public blackList; address[] public whiteList; mapping(address => PledgeType[]) public pledgeDestoryRecords; uint256 public invitationRate; uint256 public nextTokenId; bool public pledgeStatus; uint256 public dayTime; function initialize(address nftAddress, address poolAddress) public initializer { nftContract = NFT(nftAddress); poolContract = Pool(poolAddress); nextTokenId = 1; invitationRate = 1; pledgeStatus = false; dayTime = 1 days; productInfo.push(ProductInfo(180, 14)); productInfo.push(ProductInfo(270, 15)); productInfo.push(ProductInfo(360, 16)); } modifier onlyAdmin (){ address[] memory _admins = nftContract.getAdmin(); bool _isAdmin = Utils.isAdmin(_admins, msg.sender); require(_isAdmin == true,"For administrators only"); _; } modifier onlyBlacks (){ bool _isBlack = Utils.isAdmin(blackList, msg.sender); require(_isBlack == false,"You've been blacklisted."); _; } function getPledgeRecords(address _owner) public view returns(PledgeType[] memory){ return pledgeRecords[_owner]; } function getPledgeWithdrawRecord(address _owner) public view returns(PledgeWithdrawRecordType[] memory){ return pledgeWithdrawRecord[_owner]; } function getInvitationWithdrawRecord(address _owner) public view returns(InvitationWithdrawRecordType[] memory){ return invitationWithdrawRecord[_owner]; } function getPledgeDestoryRecords(address _owner) public view returns(PledgeType[] memory){ return pledgeDestoryRecords[_owner]; } function getProductInfo() public view returns (ProductInfo[] memory) { return productInfo; } function getOwnerAllPledgeInfo( address _ownerAddress ) public view returns (PledgeType[] memory result) { uint256[] memory tokens = getOwnerAllTokens(_ownerAddress); result = new PledgeType[](tokens.length); for (uint256 i = 0; i < tokens.length; i++) { result[i] = pledges[tokens[i]]; } return result; } function bindRecommend(address _referrer) internal { require(recommendObj[msg.sender].key == address(0),"Already have recommenders"); require(msg.sender != _referrer ,"You can't recommend yourself."); require(recommendObj[_referrer].referrer != msg.sender,"Can't recommend each other"); recommendObj[msg.sender] = RecommendObjType( msg.sender, _referrer, block.timestamp, 0 ); invitationAddress[_referrer].push(msg.sender); } function setInvitationIncomes(PledgeType memory _pledge) internal { address recommendAddr = recommendObj[msg.sender].referrer; PledgeType memory _invitationPledge = _pledge; _invitationPledge.rate = invitationRate; uint256 amount = _invitationPledge.pledgeAmount; uint256 rate = _invitationPledge.rate; uint256 day = _invitationPledge.pledgeDay; uint256 tokenId = _invitationPledge.tokenId; uint256 contribute = (((amount * rate) / 365) * day) / 100; recommendObj[msg.sender].contribute += contribute; if (invitationTokens[recommendAddr].length == 0) { invitationTokens[recommendAddr] = new uint256[](0); } if (invitationAddress[recommendAddr].length == 0) { invitationAddress[recommendAddr] = new address[](0); } invitationTokens[recommendAddr].push(tokenId); invitationPledges[tokenId] = _invitationPledge; } function pledge(uint256 amount, uint256 index, address _referrer) external onlyBlacks { bool _isWhite = Utils.isAdmin(whiteList, msg.sender); if(!_isWhite){ require(pledgeStatus == false,"The pledge is closed."); } require(amount >= 1, "Minimum pledge 1FIL"); require(index < productInfo.length, "Product does not exist"); poolContract.deposit(msg.sender, amount); uint256 tokenId = nextTokenId++; if (_referrer != address(0)) { bindRecommend(_referrer); } ProductInfo memory item = productInfo[index]; uint256 startTime = block.timestamp; uint256 endTime = block.timestamp + (item.day * dayTime); nftContract.mint(msg.sender, tokenId); PledgeType memory _pledge = PledgeType(tokenId,startTime,endTime,startTime,amount,item.rate,item.day,msg.sender,false); pledges[tokenId] = _pledge; pledgeRecords[msg.sender].push(_pledge); if (recommendObj[msg.sender].key != address(0)) { setInvitationIncomes(_pledge); } } function getOwnerAllTokens( address _ownerAddress ) public view returns (uint256[] memory) { uint256 nftAmount = nftContract.balanceOf(_ownerAddress); uint256[] memory tokens = new uint256[](nftAmount); for (uint256 i = 0; i < nftAmount; i++) { tokens[i] = nftContract.tokenOfOwnerByIndex(_ownerAddress, i); } return tokens; } function calculateInterest( PledgeType memory _pledge ) public view returns (uint256) { uint256 total_amount = _pledge.pledgeAmount; uint256 currentTime = block.timestamp; uint256 withdrawTime = _pledge.withdrawTime; uint256 rate = _pledge.rate; if(_pledge.endTime < currentTime){ currentTime = _pledge.endTime; } uint256 daysPassed = (currentTime - withdrawTime) / dayTime; uint256 dailyShare = (((total_amount * rate) / 365) * daysPassed) / 100; return dailyShare; } function getWithdrawbleAmount( address _owner ) public view returns (uint256) { uint256[] memory tokens = getOwnerAllTokens(_owner); if (tokens.length <= 0) return 0; uint256 _total_interest = 0; for (uint256 i = 0; i < tokens.length; i++) { uint256 _tokenId = tokens[i]; PledgeType memory _item = pledges[_tokenId]; _total_interest += calculateInterest(_item); } return _total_interest; } function withdraAllInterest() external onlyBlacks { uint256 _total_interest = getWithdrawbleAmount(msg.sender); require(_total_interest > 0, "Withdrawal amount must be greater than 0"); uint256[] memory tokens = getOwnerAllTokens(msg.sender); for(uint256 i=0;i 0, "Withdrawal amount must be greater than 0"); uint256 _withdrawTime = block.timestamp; if(data.endTime < _withdrawTime){ _withdrawTime = data.endTime; } poolContract.withdraw(msg.sender, _interest); pledges[tokenId].withdrawTime = _withdrawTime; pledgeWithdrawRecord[msg.sender].push(PledgeWithdrawRecordType(_interest,block.timestamp,data.tokenId,data.pledgeDay,1)); } function getOwnerInvitationPledges( address addr ) public view returns (PledgeType[] memory result) { uint256[] memory _tokens = invitationTokens[addr]; result = new PledgeType[](_tokens.length); for (uint256 i = 0; i < _tokens.length; i++) { uint256 _tokenId = _tokens[i]; result[i] = invitationPledges[_tokenId]; } return result; } function getOwnerAllInvitationWithdrawAmout(address owner) public view returns(uint256){ uint256 _income = 0; PledgeType[] memory _pledges = getOwnerInvitationPledges(owner); for(uint256 i=0;i<_pledges.length;i++){ PledgeType memory data = _pledges[i]; _income += calculateInterest(data); } return _income; } function withdraInvitationAllInterest() external onlyBlacks { uint256 _total_interest = getOwnerAllInvitationWithdrawAmout(msg.sender); require(_total_interest > 0, "Withdrawal amount must be greater than 0"); uint256[] memory tokens = invitationTokens[msg.sender]; for(uint256 i=0;i 0, "Withdrawal amount must be greater than 0"); uint256 _withdrawTime = block.timestamp; if(data.endTime < _withdrawTime){ _withdrawTime = data.endTime; } poolContract.withdraw(msg.sender, _interest); uint256 currentTime = block.timestamp; invitationPledges[tokenId].withdrawTime = _withdrawTime; invitationWithdrawRecord[msg.sender].push(InvitationWithdrawRecordType(_interest,currentTime)); } function getAllInvitationMember( address addr ) public view returns (RecommendObjType[] memory result) { address[] memory _addresss = invitationAddress[addr]; result = new RecommendObjType[](_addresss.length); for (uint256 i = 0; i < _addresss.length; i++) { result[i] = recommendObj[_addresss[i]]; } return result; } function getCurrentTime() public view returns (uint256){ uint256 currentTime = block.timestamp; return currentTime; } function destroyPledge(uint256 tokenId) external onlyBlacks{ address _owner = nftContract.ownerOf(tokenId); require(_owner == msg.sender,"You're not a contract owner."); uint256 currentTime = block.timestamp; PledgeType memory data = pledges[tokenId]; require(data.endTime < currentTime,"Unexpired Pledge Agreement"); nftContract.transferFrom(msg.sender,address(this), tokenId); poolContract.withdraw(msg.sender, data.pledgeAmount); data.withdrawTime = currentTime; pledgeDestoryRecords[msg.sender].push(data); } function setInvitationRate(uint256 _rate) external onlyAdmin{ invitationRate = _rate; } function setPledgeStatus(bool _status) external onlyAdmin { pledgeStatus = _status; } function addBlackOrWhiteList(address[] memory _blacks,uint256 _type) external onlyAdmin { require(_blacks.length>0,"Enter at least one address"); if(_type == 1){ address[] memory blacks = Utils.addAdmin(blackList, _blacks); blackList = blacks; } if(_type == 2){ address[] memory whites = Utils.addAdmin(whiteList, _blacks); whiteList = whites; } } function delBlackOrWhiteList(address[] memory _blacks,uint256 _type) external onlyAdmin{ require(_blacks.length>0,"Enter at least one address"); if(_type == 1){ address[] memory blacks = Utils.deleteAdmin(blackList, _blacks); blackList = blacks; } if(_type == 2){ address[] memory whites = Utils.deleteAdmin(whiteList, _blacks); whiteList = whites; } } function getBlackOrWhiteList(uint256 _type) public view returns (address[] memory) { return _type == 1 ? blackList : whiteList; } function setProductInfo(uint256[] memory _days,uint256[] memory _rates) external onlyAdmin { delete productInfo; for(uint256 i=0;i<3;i++){ productInfo.push(ProductInfo(_days[i],_rates[i])); } } function getDetails(uint256 tokenId,uint256 _type) public view returns(PledgeType memory){ if(_type == 1){ return pledges[tokenId]; } return invitationPledges[tokenId]; } function addNftBalcks(uint256[] memory _tokenIds) external onlyAdmin { require(_tokenIds.length > 0,"Parameter is empty"); nftContract.addBlacks(_tokenIds); for(uint256 i=0;i<_tokenIds.length;i++){ uint256 _id = _tokenIds[i]; require(pledges[_id].sender != address(0), "NFT ID does not exist"); require(!pledges[_id].isBlack,"NFT ID already exists"); pledges[_id].isBlack = true; } } function deleteNftBlacks(uint256[] memory _ids) external onlyAdmin { require(_ids.length > 0,"Parameter is empty"); nftContract.delBlacks(_ids); for(uint256 i=0;i<_ids.length;i++){ uint256 _id = _ids[i]; require(pledges[_id].sender != address(0), "NFT ID does not exist"); pledges[_id].isBlack = false; } } }