Skip to content

Commit

Permalink
Feature/microcredit manager (#155)
Browse files Browse the repository at this point in the history
Co-authored-by: Eduard Dumea <[email protected]>
  • Loading branch information
dumedco and Eduard Dumea committed Feb 22, 2024
1 parent 37a4849 commit 0042d08
Show file tree
Hide file tree
Showing 32 changed files with 4,403 additions and 284 deletions.
120 changes: 97 additions & 23 deletions contracts/donationMiner/DonationMinerImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -659,29 +659,41 @@ contract DonationMinerImplementation is
external
view
override
whenStarted
whenNotPaused
returns (uint256)
{
return _estimateClaimableReward(_donorAddress, 0);
return _estimateDonorClaimableReward(_donorAddress, 0);
}

/**
* @notice Calculates the estimate reward of a donor for the next x reward periods
*
* @param _donorAddress address of the donor
*
* @return uint256 reward that donor will receive in current reward period if there isn't another donation
* @return uint256 the estimate reward donor will receive after against periods based on the current donations and stakings
*/
function estimateClaimableRewardAdvance(address _donorAddress)
external
view
override
whenStarted
whenNotPaused
returns (uint256)
{
return _estimateClaimableReward(_donorAddress, againstPeriods);
return _estimateDonorClaimableReward(_donorAddress, againstPeriods);
}

/**
* @notice Calculates the estimate reward of a possible new donation for the next againstPeriods reward periods
*
* @param _donationAmount amount of the donation
*
* @return uint256 the estimate reward donor will receive after against periods based on the current donations and stakings
*/
function estimateNewDonationClaimableRewardAdvance(uint256 _donationAmount)
external
view
override
returns (uint256)
{
return _estimateNewDonationClaimableReward(_donationAmount, againstPeriods);
}

/**
Expand All @@ -693,8 +705,6 @@ contract DonationMinerImplementation is
external
view
override
whenStarted
whenNotPaused
returns (uint256)
{
uint256 _donorAmount;
Expand All @@ -720,8 +730,6 @@ contract DonationMinerImplementation is
external
view
override
whenStarted
whenNotPaused
returns (uint256)
{
uint256 _stakeholderAmount = staking.stakeholderAmount(_stakeholderAddress);
Expand All @@ -730,15 +738,15 @@ contract DonationMinerImplementation is
}

return
(1e18 * 365100 * _estimateClaimableReward(_stakeholderAddress, 0)) / _stakeholderAmount;
(1e18 * 365100 * _estimateDonorClaimableReward(_stakeholderAddress, 0)) / _stakeholderAmount;
}

/**
* @notice Calculates the APR
*
* @return uint256 APR
*/
function generalApr() public view override whenStarted whenNotPaused returns (uint256) {
function generalApr() public view override returns (uint256) {
uint256 _donorAmount;
uint256 _totalAmount;

Expand Down Expand Up @@ -773,8 +781,7 @@ contract DonationMinerImplementation is
}

/**
* @dev Calculate all donations on the last X epochs as well as everyone
* else in the same period.
* @dev Calculate all donations on the last X epochs as well as everyone else in the same period.
*
* @param _donorAddress address of the donor
*
Expand Down Expand Up @@ -1058,6 +1065,10 @@ contract DonationMinerImplementation is
{
Donor storage _donor = donors[_donorAddress];

if (_donor.lastClaimPeriod == 0 && _donor.rewardPeriodsCount == 0) {
return (0, 0);
}

// _index is the last reward period number for which the donor claimed his reward
uint256 _index = _donor.lastClaimPeriod + 1;

Expand Down Expand Up @@ -1211,24 +1222,44 @@ contract DonationMinerImplementation is
* @param _inAdvanceRewardPeriods number of reward periods in front
* if _inAdvanceRewardPeriods is 0 the method returns
* the estimated reward for current reward period
* @return uint256 reward that donor will receive in current reward period if there isn't another donation
* @return uint256 the estimate reward donor will receive after against periods based on the current donations and stakings
*/
function _estimateClaimableReward(address _donorAddress, uint256 _inAdvanceRewardPeriods)
function _estimateDonorClaimableReward(address _donorAddress, uint256 _inAdvanceRewardPeriods)
internal
view
returns (uint256)
{
uint256 _currentPeriodReward = _calculateCurrentPeriodReward();
uint256 _totalReward = _currentPeriodReward;
Donor storage _donor = donors[_donorAddress];

while (_inAdvanceRewardPeriods > 0) {
_currentPeriodReward = (_currentPeriodReward * decayNumerator) / decayDenominator;
_totalReward += _currentPeriodReward;
_inAdvanceRewardPeriods--;
if (_inAdvanceRewardPeriods > currentRewardPeriodNumber() - _donor.rewardPeriods[_donor.rewardPeriodsCount]) {
_inAdvanceRewardPeriods -= (currentRewardPeriodNumber() - _donor.rewardPeriods[_donor.rewardPeriodsCount]);
} else {
_inAdvanceRewardPeriods = 0;
}
uint256 _totalReward = _calculateRewardForTheNextXPeriods(_inAdvanceRewardPeriods);

return _calculateDonorShare(_donorAddress, _totalReward);
}

/**
* @notice Calculates the estimate reward for a new possible donation
*
* @param _donationAmount amount of the new possible donation
* @param _inAdvanceRewardPeriods number of reward periods in front
* if _inAdvanceRewardPeriods is 0 the method returns
* the estimated reward for current reward period
* @return uint256 the estimate reward donor will receive after against periods based on the current donations and stakings
*/
function _estimateNewDonationClaimableReward(uint256 _donationAmount, uint256 _inAdvanceRewardPeriods)
internal
view
returns (uint256)
{
uint256 _totalReward = _calculateRewardForTheNextXPeriods(_inAdvanceRewardPeriods);

return _calculateNewDonationShare(_donationAmount, _totalReward);
}

/**
* @notice Calculates a donor share based on the donations and stakes from the last x rewardPeriods
*
Expand Down Expand Up @@ -1258,6 +1289,36 @@ contract DonationMinerImplementation is
(_totalAmount * _stakingDonationRatio + staking.currentTotalAmount());
}

/**
* @notice Calculates a new donation share based on the donations and stakes from the last x rewardPeriods
*
*
* @return uint256 the estimate reward donor will receive after against periods based on the current donations and stakings
*/
function _calculateNewDonationShare(uint256 _donationAmount, uint256 _total)
internal
view
returns (uint256)
{
uint256 _totalAmount;

(, _totalAmount) = lastPeriodsDonations(msg.sender);

_totalAmount += _donationAmount;

uint256 totalStakeAmount = staking.SPACT().totalSupply();
if (totalStakeAmount == 0 && _totalAmount == 0) {
return 0;
}

uint256 _stakingDonationRatio = stakingDonationRatio > 0 ? stakingDonationRatio : 1;

return
(_total *
(_donationAmount * _stakingDonationRatio)) /
(_totalAmount * _stakingDonationRatio + staking.currentTotalAmount());
}

function _calculateCurrentPeriodReward() internal view returns (uint256) {
uint256 _currentRewardPeriodNumber = currentRewardPeriodNumber();

Expand All @@ -1267,4 +1328,17 @@ contract DonationMinerImplementation is

return _rewardPerBlock * rewardPeriodSize;
}

function _calculateRewardForTheNextXPeriods(uint256 _inAdvanceRewardPeriods) internal view returns(uint256) {
uint256 _currentPeriodReward = _calculateCurrentPeriodReward();
uint256 _totalReward = _currentPeriodReward;

while (_inAdvanceRewardPeriods > 0) {
_currentPeriodReward = (_currentPeriodReward * decayNumerator) / decayDenominator;
_totalReward += _currentPeriodReward;
_inAdvanceRewardPeriods--;
}

return _totalReward;
}
}
1 change: 1 addition & 0 deletions contracts/donationMiner/interfaces/IDonationMiner.sol
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ interface IDonationMiner {
function calculateClaimableRewardsByPeriodNumber(address _donor, uint256 _lastPeriodNumber) external returns (uint256);
function estimateClaimableReward(address _donor) external view returns (uint256);
function estimateClaimableRewardAdvance(address _donor) external view returns (uint256);
function estimateNewDonationClaimableRewardAdvance(uint256 _donationAmount) external view returns (uint256);
function estimateClaimableRewardByStaking(address _donor) external view returns (uint256);
function apr(address _stakeholderAddress) external view returns (uint256);
function generalApr() external view returns (uint256);
Expand Down
104 changes: 79 additions & 25 deletions contracts/microcredit/MicrocreditImplementation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ contract MicrocreditImplementation is
using SafeERC20Upgradeable for IERC20;
using EnumerableSet for EnumerableSet.AddressSet;

event MaintainerAdded(
address indexed maintainerAddress
);

event MaintainerRemoved(address indexed maintainerRemovedAddress);

event ManagerAdded(
address indexed managerAddress,
uint256 lentAmountLimit
Expand Down Expand Up @@ -76,6 +82,13 @@ contract MicrocreditImplementation is
_;
}

modifier onlyMaintainers() {
require(
_maintainerList.contains(msg.sender) || msg.sender == owner(),
"Microcredit: caller is not a maintainer");
_;
}

/**
* @notice Used to initialize the Microcredit contract
*
Expand Down Expand Up @@ -140,6 +153,23 @@ contract MicrocreditImplementation is
return _walletList.at(_index);
}

/**
* @notice Returns an address from the maintainerList
*
* @param _index index value
* @return address of the maintainer
*/
function maintainerListAt(uint256 _index) external view override returns (address) {
return _maintainerList.at(_index);
}

/**
* @notice Returns the length of the maintainerList
*/
function maintainerListLength() external view override returns (uint256) {
return _maintainerList.length();
}

/**
* @notice Returns an address from the managerList
*
Expand Down Expand Up @@ -255,6 +285,44 @@ contract MicrocreditImplementation is
uniswapRouter = _newUniswapRouter;
}

function updateMicrocreditManager(IMicrocreditManager _newMicrocreditManager) external override onlyOwner {
microcreditManager = _newMicrocreditManager;
}

/**
* @notice Adds maintainers
*
* @param _maintainerAddresses addresses of the maintainers
*/
function addMaintainers(address[] calldata _maintainerAddresses) external override onlyOwner {
uint256 _length = _maintainerAddresses.length;
uint256 _index;

for (_index = 0; _index < _length; _index++) {
_maintainerList.add(_maintainerAddresses[_index]);

emit MaintainerAdded(
_maintainerAddresses[_index]
);
}
}

/**
* @notice Removes maintainers
*
* @param _maintainerAddresses addresses of the maintainers
*/
function removeMaintainers(address[] calldata _maintainerAddresses) external override onlyOwner {
uint256 _length = _maintainerAddresses.length;
uint256 _maintainerId;

for (_maintainerId = 0; _maintainerId < _length; _maintainerId++) {
_maintainerList.remove(_maintainerAddresses[_maintainerId]);

emit MaintainerRemoved(_maintainerAddresses[_maintainerId]);
}
}

/**
* @notice Adds managers
*
Expand All @@ -263,7 +331,7 @@ contract MicrocreditImplementation is
function addManagers(
address[] calldata _managerAddresses,
uint256[] calldata _lentAmountLimits
) external override onlyOwner {
) external override onlyMaintainers {
uint256 _length = _managerAddresses.length;
uint256 _index;

Expand All @@ -284,7 +352,7 @@ contract MicrocreditImplementation is
*
* @param _managerAddresses addresses of the managers
*/
function removeManagers(address[] calldata _managerAddresses) external override onlyOwner {
function removeManagers(address[] calldata _managerAddresses) external override onlyMaintainers {
uint256 _length = _managerAddresses.length;
uint256 _managerId;

Expand Down Expand Up @@ -319,26 +387,6 @@ contract MicrocreditImplementation is
_addLoan(_userAddress, _tokenAddress, _amount, _period, _dailyInterest, _claimDeadline);
}

/**
* @notice Adds a loan
*
* @param _userAddress address of the user
* @param _amount amount of the loan
* @param _period period of the loan
* @param _dailyInterest daily interest of the loan
* @param _claimDeadline claim deadline of the loan
*/
function addLoan(
address _userAddress,
uint256 _amount,
uint256 _period,
uint256 _dailyInterest,
uint256 _claimDeadline
) external override onlyManagers {
_addLoan(_userAddress, address(cUSD), _amount, _period, _dailyInterest, _claimDeadline);
}


/**
* @notice Adds multiples loans
*
Expand Down Expand Up @@ -384,8 +432,8 @@ contract MicrocreditImplementation is

for (_index = 0; _index < _loansNumber; _index++) {
_addLoan(
_tokenAddresses[_index],
_userAddresses[_index],
_tokenAddresses[_index],
_amounts[_index],
_periods[_index],
_dailyInterests[_index],
Expand Down Expand Up @@ -595,8 +643,14 @@ contract MicrocreditImplementation is

_loan.lastComputedDate = _loan.lastComputedDate + _days * 86400;

if (_loan.lastComputedDebt == 0 && address(donationMiner) != address(0)) {
donationMiner.donateVirtual(_loan.amountRepayed - _loan.amountBorrowed, msg.sender);
if (_loan.lastComputedDebt == 0) {
if (address(microcreditManager) != address(0)) {
microcreditManager.addCompletedLoanToManager(_loan.managerAddress, msg.sender, _loanId);
}

if (address(donationMiner) != address(0)) {
donationMiner.donateVirtual(_loan.amountRepayed - _loan.amountBorrowed, msg.sender);
}
}

emit RepaymentAdded(msg.sender, _loanId, _repaymentAmount, _loan.lastComputedDebt);
Expand Down
Loading

0 comments on commit 0042d08

Please sign in to comment.