You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

370 lines
15 KiB

7 months ago
  1. // SPDX-License-Identifier: MIT
  2. pragma solidity ^0.8.19;
  3. import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
  4. import "./NFT.sol";
  5. import "./Pool.sol";
  6. import "./PledgeType.sol";
  7. import "./Utils.sol";
  8. contract Pledge is Initializable {
  9. NFT nftContract;
  10. Pool poolContract;
  11. ProductInfo[] public productInfo;
  12. mapping(uint256 => PledgeType) pledges;
  13. mapping(address => PledgeType[]) public pledgeRecords;
  14. mapping(uint256 => PledgeType) public invitationPledges;
  15. mapping(address => RecommendObjType) public recommendObj;
  16. mapping(address => uint256[]) public invitationTokens;
  17. mapping(address => address[]) public invitationAddress;
  18. mapping(address => PledgeWithdrawRecordType[]) public pledgeWithdrawRecord;
  19. mapping(address => InvitationWithdrawRecordType[]) public invitationWithdrawRecord;
  20. address[] public blackList;
  21. address[] public whiteList;
  22. mapping(address => PledgeType[]) public pledgeDestoryRecords;
  23. uint256 public invitationRate;
  24. uint256 public nextTokenId;
  25. bool public pledgeStatus;
  26. uint256 public dayTime;
  27. function initialize(address nftAddress, address poolAddress) public initializer {
  28. nftContract = NFT(nftAddress);
  29. poolContract = Pool(poolAddress);
  30. nextTokenId = 1;
  31. invitationRate = 1;
  32. pledgeStatus = false;
  33. dayTime = 1 days;
  34. productInfo.push(ProductInfo(180, 14));
  35. productInfo.push(ProductInfo(270, 15));
  36. productInfo.push(ProductInfo(360, 16));
  37. }
  38. modifier onlyAdmin (){
  39. address[] memory _admins = nftContract.getAdmin();
  40. bool _isAdmin = Utils.isAdmin(_admins, msg.sender);
  41. require(_isAdmin == true,"For administrators only");
  42. _;
  43. }
  44. modifier onlyBlacks (){
  45. bool _isBlack = Utils.isAdmin(blackList, msg.sender);
  46. require(_isBlack == false,"You've been blacklisted.");
  47. _;
  48. }
  49. function getPledgeRecords(address _owner) public view returns(PledgeType[] memory){
  50. return pledgeRecords[_owner];
  51. }
  52. function getPledgeWithdrawRecord(address _owner) public view returns(PledgeWithdrawRecordType[] memory){
  53. return pledgeWithdrawRecord[_owner];
  54. }
  55. function getInvitationWithdrawRecord(address _owner) public view returns(InvitationWithdrawRecordType[] memory){
  56. return invitationWithdrawRecord[_owner];
  57. }
  58. function getPledgeDestoryRecords(address _owner) public view returns(PledgeType[] memory){
  59. return pledgeDestoryRecords[_owner];
  60. }
  61. function getProductInfo() public view returns (ProductInfo[] memory) {
  62. return productInfo;
  63. }
  64. function getOwnerAllPledgeInfo( address _ownerAddress ) public view returns (PledgeType[] memory result) {
  65. uint256[] memory tokens = getOwnerAllTokens(_ownerAddress);
  66. result = new PledgeType[](tokens.length);
  67. for (uint256 i = 0; i < tokens.length; i++) {
  68. result[i] = pledges[tokens[i]];
  69. }
  70. return result;
  71. }
  72. function bindRecommend(address _referrer) internal {
  73. require(recommendObj[msg.sender].key == address(0),"Already have recommenders");
  74. require(msg.sender != _referrer ,"You can't recommend yourself.");
  75. require(recommendObj[_referrer].referrer != msg.sender,"Can't recommend each other");
  76. recommendObj[msg.sender] = RecommendObjType(
  77. msg.sender,
  78. _referrer,
  79. block.timestamp,
  80. 0
  81. );
  82. invitationAddress[_referrer].push(msg.sender);
  83. }
  84. function setInvitationIncomes(PledgeType memory _pledge) internal {
  85. address recommendAddr = recommendObj[msg.sender].referrer;
  86. PledgeType memory _invitationPledge = _pledge;
  87. _invitationPledge.rate = invitationRate;
  88. uint256 amount = _invitationPledge.pledgeAmount;
  89. uint256 rate = _invitationPledge.rate;
  90. uint256 day = _invitationPledge.pledgeDay;
  91. uint256 tokenId = _invitationPledge.tokenId;
  92. uint256 contribute = (((amount * rate) / 365) * day) / 100;
  93. recommendObj[msg.sender].contribute += contribute;
  94. if (invitationTokens[recommendAddr].length == 0) {
  95. invitationTokens[recommendAddr] = new uint256[](0);
  96. }
  97. if (invitationAddress[recommendAddr].length == 0) {
  98. invitationAddress[recommendAddr] = new address[](0);
  99. }
  100. invitationTokens[recommendAddr].push(tokenId);
  101. invitationPledges[tokenId] = _invitationPledge;
  102. }
  103. function pledge(uint256 amount, uint256 index, address _referrer) external onlyBlacks {
  104. bool _isWhite = Utils.isAdmin(whiteList, msg.sender);
  105. if(!_isWhite){
  106. require(pledgeStatus == false,"The pledge is closed.");
  107. }
  108. require(amount >= 1, "Minimum pledge 1FIL");
  109. require(index < productInfo.length, "Product does not exist");
  110. poolContract.deposit(msg.sender, amount);
  111. uint256 tokenId = nextTokenId++;
  112. if (_referrer != address(0)) {
  113. bindRecommend(_referrer);
  114. }
  115. ProductInfo memory item = productInfo[index];
  116. uint256 startTime = block.timestamp;
  117. uint256 endTime = block.timestamp + (item.day * dayTime);
  118. nftContract.mint(msg.sender, tokenId);
  119. PledgeType memory _pledge = PledgeType(tokenId,startTime,endTime,startTime,amount,item.rate,item.day,msg.sender,false);
  120. pledges[tokenId] = _pledge;
  121. pledgeRecords[msg.sender].push(_pledge);
  122. if (recommendObj[msg.sender].key != address(0)) {
  123. setInvitationIncomes(_pledge);
  124. }
  125. }
  126. function getOwnerAllTokens( address _ownerAddress ) public view returns (uint256[] memory) {
  127. uint256 nftAmount = nftContract.balanceOf(_ownerAddress);
  128. uint256[] memory tokens = new uint256[](nftAmount);
  129. for (uint256 i = 0; i < nftAmount; i++) {
  130. tokens[i] = nftContract.tokenOfOwnerByIndex(_ownerAddress, i);
  131. }
  132. return tokens;
  133. }
  134. function calculateInterest( PledgeType memory _pledge ) public view returns (uint256) {
  135. uint256 total_amount = _pledge.pledgeAmount;
  136. uint256 currentTime = block.timestamp;
  137. uint256 withdrawTime = _pledge.withdrawTime;
  138. uint256 rate = _pledge.rate;
  139. if(_pledge.endTime < currentTime){
  140. currentTime = _pledge.endTime;
  141. }
  142. uint256 daysPassed = (currentTime - withdrawTime) / dayTime;
  143. uint256 dailyShare = (((total_amount * rate) / 365) * daysPassed) / 100;
  144. return dailyShare;
  145. }
  146. function getWithdrawbleAmount( address _owner ) public view returns (uint256) {
  147. uint256[] memory tokens = getOwnerAllTokens(_owner);
  148. if (tokens.length <= 0) return 0;
  149. uint256 _total_interest = 0;
  150. for (uint256 i = 0; i < tokens.length; i++) {
  151. uint256 _tokenId = tokens[i];
  152. PledgeType memory _item = pledges[_tokenId];
  153. _total_interest += calculateInterest(_item);
  154. }
  155. return _total_interest;
  156. }
  157. function withdraAllInterest() external onlyBlacks {
  158. uint256 _total_interest = getWithdrawbleAmount(msg.sender);
  159. require(_total_interest > 0, "Withdrawal amount must be greater than 0");
  160. uint256[] memory tokens = getOwnerAllTokens(msg.sender);
  161. for(uint256 i=0;i<tokens.length;i++){
  162. uint256 _withdrawTime = block.timestamp;
  163. uint256 _tokenId = tokens[i];
  164. require(!pledges[_tokenId].isBlack,"Unable to withdraw, the NFT Tokend is blacklisted.");
  165. if(pledges[_tokenId].endTime < _withdrawTime){
  166. _withdrawTime = pledges[_tokenId].endTime;
  167. }
  168. pledges[_tokenId].withdrawTime = _withdrawTime;
  169. }
  170. poolContract.withdraw(msg.sender, _total_interest);
  171. pledgeWithdrawRecord[msg.sender].push(PledgeWithdrawRecordType(_total_interest,block.timestamp,0,0,2));
  172. }
  173. function withdrawInterest(uint256 tokenId) external onlyBlacks {
  174. address _owner = nftContract.ownerOf(tokenId);
  175. require(_owner == msg.sender,"It's not a contract.");
  176. PledgeType memory data = pledges[tokenId];
  177. require(!data.isBlack,"Unable to withdraw,the NFT Tokend is blacklisted.");
  178. uint256 _interest = calculateInterest(data);
  179. require(_interest > 0, "Withdrawal amount must be greater than 0");
  180. uint256 _withdrawTime = block.timestamp;
  181. if(data.endTime < _withdrawTime){
  182. _withdrawTime = data.endTime;
  183. }
  184. poolContract.withdraw(msg.sender, _interest);
  185. pledges[tokenId].withdrawTime = _withdrawTime;
  186. pledgeWithdrawRecord[msg.sender].push(PledgeWithdrawRecordType(_interest,block.timestamp,data.tokenId,data.pledgeDay,1));
  187. }
  188. function getOwnerInvitationPledges( address addr ) public view returns (PledgeType[] memory result) {
  189. uint256[] memory _tokens = invitationTokens[addr];
  190. result = new PledgeType[](_tokens.length);
  191. for (uint256 i = 0; i < _tokens.length; i++) {
  192. uint256 _tokenId = _tokens[i];
  193. result[i] = invitationPledges[_tokenId];
  194. }
  195. return result;
  196. }
  197. function getOwnerAllInvitationWithdrawAmout(address owner) public view returns(uint256){
  198. uint256 _income = 0;
  199. PledgeType[] memory _pledges = getOwnerInvitationPledges(owner);
  200. for(uint256 i=0;i<_pledges.length;i++){
  201. PledgeType memory data = _pledges[i];
  202. _income += calculateInterest(data);
  203. }
  204. return _income;
  205. }
  206. function withdraInvitationAllInterest() external onlyBlacks {
  207. uint256 _total_interest = getOwnerAllInvitationWithdrawAmout(msg.sender);
  208. require(_total_interest > 0, "Withdrawal amount must be greater than 0");
  209. uint256[] memory tokens = invitationTokens[msg.sender];
  210. for(uint256 i=0;i<tokens.length;i++){
  211. uint256 _withdrawTime = block.timestamp;
  212. uint256 _tokenId = tokens[i];
  213. if(invitationPledges[_tokenId].endTime < _withdrawTime){
  214. _withdrawTime = invitationPledges[_tokenId].endTime;
  215. }
  216. invitationPledges[_tokenId].withdrawTime = _withdrawTime;
  217. }
  218. uint256 currentTime = block.timestamp;
  219. poolContract.withdraw(msg.sender, _total_interest);
  220. invitationWithdrawRecord[msg.sender].push(InvitationWithdrawRecordType(_total_interest,currentTime));
  221. }
  222. function withdrawInvitationInterest(uint256 tokenId) external onlyBlacks {
  223. uint256[] memory _tokens = invitationTokens[msg.sender];
  224. bool _owner = false;
  225. for(uint256 i=0;i<_tokens.length;i++){
  226. uint256 _tokenId = _tokens[i];
  227. if(_tokenId == tokenId){
  228. _owner = true;
  229. break;
  230. }
  231. }
  232. require(_owner == true,"It's not a contract.");
  233. PledgeType memory data = invitationPledges[tokenId];
  234. uint256 _interest = calculateInterest(data);
  235. require(_interest > 0, "Withdrawal amount must be greater than 0");
  236. uint256 _withdrawTime = block.timestamp;
  237. if(data.endTime < _withdrawTime){
  238. _withdrawTime = data.endTime;
  239. }
  240. poolContract.withdraw(msg.sender, _interest);
  241. uint256 currentTime = block.timestamp;
  242. invitationPledges[tokenId].withdrawTime = _withdrawTime;
  243. invitationWithdrawRecord[msg.sender].push(InvitationWithdrawRecordType(_interest,currentTime));
  244. }
  245. function getAllInvitationMember( address addr ) public view returns (RecommendObjType[] memory result) {
  246. address[] memory _addresss = invitationAddress[addr];
  247. result = new RecommendObjType[](_addresss.length);
  248. for (uint256 i = 0; i < _addresss.length; i++) {
  249. result[i] = recommendObj[_addresss[i]];
  250. }
  251. return result;
  252. }
  253. function getCurrentTime() public view returns (uint256){
  254. uint256 currentTime = block.timestamp;
  255. return currentTime;
  256. }
  257. function destroyPledge(uint256 tokenId) external onlyBlacks{
  258. address _owner = nftContract.ownerOf(tokenId);
  259. require(_owner == msg.sender,"You're not a contract owner.");
  260. uint256 currentTime = block.timestamp;
  261. PledgeType memory data = pledges[tokenId];
  262. require(data.endTime < currentTime,"Unexpired Pledge Agreement");
  263. nftContract.transferFrom(msg.sender,address(this), tokenId);
  264. poolContract.withdraw(msg.sender, data.pledgeAmount);
  265. data.withdrawTime = currentTime;
  266. pledgeDestoryRecords[msg.sender].push(data);
  267. }
  268. function setInvitationRate(uint256 _rate) external onlyAdmin{
  269. invitationRate = _rate;
  270. }
  271. function setPledgeStatus(bool _status) external onlyAdmin {
  272. pledgeStatus = _status;
  273. }
  274. function addBlackOrWhiteList(address[] memory _blacks,uint256 _type) external onlyAdmin {
  275. require(_blacks.length>0,"Enter at least one address");
  276. if(_type == 1){
  277. address[] memory blacks = Utils.addAdmin(blackList, _blacks);
  278. blackList = blacks;
  279. }
  280. if(_type == 2){
  281. address[] memory whites = Utils.addAdmin(whiteList, _blacks);
  282. whiteList = whites;
  283. }
  284. }
  285. function delBlackOrWhiteList(address[] memory _blacks,uint256 _type) external onlyAdmin{
  286. require(_blacks.length>0,"Enter at least one address");
  287. if(_type == 1){
  288. address[] memory blacks = Utils.deleteAdmin(blackList, _blacks);
  289. blackList = blacks;
  290. }
  291. if(_type == 2){
  292. address[] memory whites = Utils.deleteAdmin(whiteList, _blacks);
  293. whiteList = whites;
  294. }
  295. }
  296. function getBlackOrWhiteList(uint256 _type) public view returns (address[] memory) {
  297. return _type == 1 ? blackList : whiteList;
  298. }
  299. function setProductInfo(uint256[] memory _days,uint256[] memory _rates) external onlyAdmin {
  300. delete productInfo;
  301. for(uint256 i=0;i<3;i++){
  302. productInfo.push(ProductInfo(_days[i],_rates[i]));
  303. }
  304. }
  305. function getDetails(uint256 tokenId,uint256 _type) public view returns(PledgeType memory){
  306. if(_type == 1){
  307. return pledges[tokenId];
  308. }
  309. return invitationPledges[tokenId];
  310. }
  311. function addNftBalcks(uint256[] memory _tokenIds) external onlyAdmin {
  312. require(_tokenIds.length > 0,"Parameter is empty");
  313. nftContract.addBlacks(_tokenIds);
  314. for(uint256 i=0;i<_tokenIds.length;i++){
  315. uint256 _id = _tokenIds[i];
  316. require(pledges[_id].sender != address(0), "NFT ID does not exist");
  317. require(!pledges[_id].isBlack,"NFT ID already exists");
  318. pledges[_id].isBlack = true;
  319. }
  320. }
  321. function deleteNftBlacks(uint256[] memory _ids) external onlyAdmin {
  322. require(_ids.length > 0,"Parameter is empty");
  323. nftContract.delBlacks(_ids);
  324. for(uint256 i=0;i<_ids.length;i++){
  325. uint256 _id = _ids[i];
  326. require(pledges[_id].sender != address(0), "NFT ID does not exist");
  327. pledges[_id].isBlack = false;
  328. }
  329. }
  330. }