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.
371 lines
15 KiB
371 lines
15 KiB
// 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<tokens.length;i++){
|
|
uint256 _withdrawTime = block.timestamp;
|
|
uint256 _tokenId = tokens[i];
|
|
require(!pledges[_tokenId].isBlack,"Unable to withdraw, the NFT Tokend is blacklisted.");
|
|
if(pledges[_tokenId].endTime < _withdrawTime){
|
|
_withdrawTime = pledges[_tokenId].endTime;
|
|
}
|
|
pledges[_tokenId].withdrawTime = _withdrawTime;
|
|
}
|
|
poolContract.withdraw(msg.sender, _total_interest);
|
|
pledgeWithdrawRecord[msg.sender].push(PledgeWithdrawRecordType(_total_interest,block.timestamp,0,0,2));
|
|
}
|
|
|
|
function withdrawInterest(uint256 tokenId) external onlyBlacks {
|
|
address _owner = nftContract.ownerOf(tokenId);
|
|
require(_owner == msg.sender,"It's not a contract.");
|
|
PledgeType memory data = pledges[tokenId];
|
|
require(!data.isBlack,"Unable to withdraw,the NFT Tokend is blacklisted.");
|
|
uint256 _interest = calculateInterest(data);
|
|
require(_interest > 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<tokens.length;i++){
|
|
uint256 _withdrawTime = block.timestamp;
|
|
uint256 _tokenId = tokens[i];
|
|
if(invitationPledges[_tokenId].endTime < _withdrawTime){
|
|
_withdrawTime = invitationPledges[_tokenId].endTime;
|
|
}
|
|
invitationPledges[_tokenId].withdrawTime = _withdrawTime;
|
|
}
|
|
uint256 currentTime = block.timestamp;
|
|
poolContract.withdraw(msg.sender, _total_interest);
|
|
invitationWithdrawRecord[msg.sender].push(InvitationWithdrawRecordType(_total_interest,currentTime));
|
|
}
|
|
|
|
function withdrawInvitationInterest(uint256 tokenId) external onlyBlacks {
|
|
uint256[] memory _tokens = invitationTokens[msg.sender];
|
|
bool _owner = false;
|
|
for(uint256 i=0;i<_tokens.length;i++){
|
|
uint256 _tokenId = _tokens[i];
|
|
if(_tokenId == tokenId){
|
|
_owner = true;
|
|
break;
|
|
}
|
|
}
|
|
require(_owner == true,"It's not a contract.");
|
|
PledgeType memory data = invitationPledges[tokenId];
|
|
uint256 _interest = calculateInterest(data);
|
|
require(_interest > 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;
|
|
}
|
|
}
|
|
|
|
}
|