Skip to content

Commit

Permalink
claimRewards functionality from users
Browse files Browse the repository at this point in the history
  • Loading branch information
newtmex committed Aug 5, 2024
1 parent 860441f commit df37dd7
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 16 deletions.
6 changes: 3 additions & 3 deletions packages/backend/contracts/housing-project/RentsModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ abstract contract RentsModule is HousingSFT, CallsSmartHousing {
/// @return The updated HousingAttributes.
function claimRentReward(
uint256 nonce
) external returns (HousingAttributes memory) {
) external returns (HousingAttributes memory, rewardshares memory) {
address caller = msg.sender;
uint256 currentRPS = rewardPerShare;

Expand All @@ -65,7 +65,7 @@ abstract contract RentsModule is HousingSFT, CallsSmartHousing {

if (totalReward == 0) {
// Fail silently
return attr;
return (attr, rewardShares);
}

require(rewardsReserve >= totalReward, "Computed rewards too large");
Expand All @@ -87,7 +87,7 @@ abstract contract RentsModule is HousingSFT, CallsSmartHousing {

housingToken.transfer(caller, rewardShares.userValue); // Send to user

return attr;
return (attr, rewardShares);
}

/// @notice Computes the amount of rent claimable for a given token.
Expand Down
27 changes: 17 additions & 10 deletions packages/backend/contracts/main/HST.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ library NewHousingStakingToken {
}
}

struct HstAttributes {
TokenPayment[] projectTokens;
uint256 projectsShareCheckpoint;
uint256 shtRewardPerShare;
uint256 shtAmount;
uint256 stakeWeight;
uint256 lkDuration;
uint256 lkShtNonce;
}

contract HousingStakingToken is SFT, Ownable {
using SafeMath for uint256;

uint256 public constant MIN_EPOCHS_LOCK = 180;
uint256 public constant MAX_EPOCHS_LOCK = 1080;

struct HstAttributes {
TokenPayment[] projectTokens;
uint256 projectsShareCheckpoint;
uint256 shtRewardPerShare;
uint256 shtAmount;
uint256 stakeWeight;
uint256 lkDuration;
uint256 lkShtNonce;
}

event MintHstToken(
address indexed to,
uint256 nonce,
Expand Down Expand Up @@ -77,4 +77,11 @@ contract HousingStakingToken is SFT, Ownable {

emit MintHstToken(caller, nonce, attr);
}

function setTokenAttributes(
uint256 nonce,
HstAttributes memory attr
) external onlyOwner {
_setTokenAttributes(nonce, abi.encode(attr));
}
}
61 changes: 60 additions & 1 deletion packages/backend/contracts/main/SmartHousing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.26;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";

import "../lib/TokenPayments.sol";
import "../modules/sht-module/SHT.sol";
Expand All @@ -14,7 +15,10 @@ import "./User.sol";

import { Distribution } from "./distribution/Storage.sol";
import { EpochsAndPeriods } from "../lib/EpochsAndPeriods.sol";
import { HousingStakingToken, NewHousingStakingToken } from "./HST.sol";
import { HousingStakingToken, NewHousingStakingToken, HstAttributes } from "./HST.sol";

import { HousingProject } from "../housing-project/HousingProject.sol";
import { rewardshares } from "../housing-project/RewardSharing.sol";

/// @title SmartHousing
/// @notice SmartHousing leverages blockchain technology to revolutionize real estate investment and development by enabling the tokenization of properties.
Expand All @@ -35,6 +39,7 @@ contract SmartHousing is
using EpochsAndPeriods for EpochsAndPeriods.Storage;
using EnumerableSet for EnumerableSet.AddressSet;
using TokenPayments for TokenPayment;
using SafeMath for uint256;

address public projectFundingAddress;
address public coinbaseAddress;
Expand Down Expand Up @@ -140,6 +145,60 @@ contract SmartHousing is
distributionStorage.enterStaking(newAttr.stakeWeight);
}

function claimRewards(uint256 hstTokenId, uint256 referrerId) external {
address caller = msg.sender;
_createOrGetUserId(caller, referrerId);

require(
hst.balanceOf(caller, hstTokenId) > 0,
"Caller does not own the hst token"
);

distributionStorage.generateRewards(epochsAndPeriodsStorage);

(uint256 claimedSHT, HstAttributes memory hstAttr) = distributionStorage
.claimRewards(
abi.decode(getRawTokenAttributes(hstTokenId), (HstAttributes))
);
uint256 rentRewards = 0;

// Claim rent rewards from HousingProjects
for (uint256 i = 0; i < hstAttr.projectTokens.length; i++) {
TokenPayment memory projectToken = hstAttr.projectTokens[i];
require(
projectToken.token != address(0),
"Invalid project address"
);

// Call the external contract's claimRentReward function
(, rewardshares memory rewardShares) = HousingProject(
projectToken.token
).claimRentReward(projectToken.nonce);

rentRewards = rentRewards.add(rewardShares.userValue);
}

// Update the attributes in the hst token
hst.setTokenAttributes(hstTokenId, hstAttr);

ERC20Burnable shtToken = ERC20Burnable(shtTokenAddress);

if (claimedSHT > 0) {
uint256 referrerValue = claimedSHT.mul(25).div(1000);
claimedSHT = claimedSHT.sub(referrerValue);

// Do referrer operations
(, address referrerAddr) = getReferrer(caller);
if (referrerAddr != address(0)) {
shtToken.transfer(referrerAddr, referrerValue);
} else {
shtToken.burn(referrerValue);
}
}

shtToken.transfer(caller, claimedSHT.add(rentRewards));
}

function projectDets(
address project
) public view returns (Distribution.ProjectDistributionData memory) {
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/contracts/main/User.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ abstract contract UserModule is IUserModule {
/// @return referrerAddress The address of the referrer, address(0) if none.
function getReferrer(
address userAddress
) external view returns (uint256 referrerId, address referrerAddress) {
) public view returns (uint256 referrerId, address referrerAddress) {
User storage user = users[userAddress];
referrerId = user.referrerId;
referrerAddress = userIdToAddress[referrerId];
Expand Down
92 changes: 91 additions & 1 deletion packages/backend/contracts/main/distribution/Storage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "../../lib/EpochsAndPeriods.sol";
import "../../housing-project/HousingProject.sol";
import "../../modules/sht-module/Economics.sol";
import { HstAttributes } from "../HST.sol";

library ProjectStakingRewards {
using SafeMath for uint256;
Expand Down Expand Up @@ -151,10 +152,99 @@ library Distribution {
self.lastFundsDispatchEpoch = currentEpoch;
}

/// @notice Claims rewards for a given attribute.
/// @param self The storage struct for the `Distribution` contract.
/// @param attr The attributes struct for which rewards are being claimed.
/// @return The total amount of rewards claimed.
function claimRewards(
Storage storage self,
HstAttributes memory attr
) internal returns (uint256, HstAttributes memory) {
uint256 shtClaimed = 0;

// Claim PT rewards
uint256 ptRewardCheckpoint = self.projectsStakingRewards.checkpoint;
if (ptRewardCheckpoint > 0) {
for (uint256 i = 0; i < attr.projectTokens.length; i++) {
shtClaimed = shtClaimed.add(
computeRewardForPT(
self,
attr.projectTokens[i],
attr.projectsShareCheckpoint,
ptRewardCheckpoint
)
);
}

if (self.projectsStakingRewards.toShare < shtClaimed) {
shtClaimed = self.projectsStakingRewards.toShare;
}
self.projectsStakingRewards.toShare = self
.projectsStakingRewards
.toShare
.sub(shtClaimed);
}

// Claim SHT rewards
uint256 shtRPS = self.shtRewardPerShare;
if (shtRPS > 0 && attr.shtRewardPerShare < shtRPS) {
uint256 shtReward = (shtRPS.sub(attr.shtRewardPerShare))
.mul(attr.stakeWeight)
.div(DIVISION_SAFETY_CONST);
if (self.shtStakingRewards < shtReward) {
shtClaimed = self.shtStakingRewards;
}
self.shtStakingRewards = self.shtStakingRewards.sub(shtReward);

shtClaimed = shtClaimed.add(shtReward);
}

// Update claim parameters
attr.shtRewardPerShare = shtRPS;
attr.projectsShareCheckpoint = ptRewardCheckpoint;

return (shtClaimed, attr);
}

/// @notice Computes the reward for a given PT (Housing Project Token).
/// @param self The storage struct for the `Distribution` contract.
/// @param tokenPayment The token payment of the housing project.
/// @param stakingCheckPoint The previous checkpoint value.
/// @param tokenCheckPoint The new checkpoint value.
/// @return reward The computed reward for the given PT.
function computeRewardForPT(
Storage storage self,
TokenPayment memory tokenPayment,
uint256 stakingCheckPoint,
uint256 tokenCheckPoint
) internal view returns (uint256 reward) {
if (stakingCheckPoint >= tokenCheckPoint) {
return 0;
}

ProjectDistributionData storage projectData = self.projectDets[
tokenPayment.token
];
require(
tokenPayment.amount <= projectData.maxShares,
"Project token amount too large"
);

uint256 shareIncrease = tokenCheckPoint.sub(stakingCheckPoint);
// Project's allocation is dynamic, as rents received chages
uint256 projectAllocation = shareIncrease
.mul(projectData.receivedRents)
.div(self.projectsTotalReceivedRents);

reward = projectAllocation.mul(tokenPayment.amount).div(
projectData.maxShares
);
}

/// @notice Enters staking for the given attributes.
/// @param self The storage struct for the `Distribution` contract.
/// @param stakeWeight The stake weight to be added.
function enterStaking(Storage storage self, uint256 stakeWeight) internal {
self.shtTotalStakeWeight = self.shtTotalStakeWeight.add(stakeWeight);
}
}
}

0 comments on commit df37dd7

Please sign in to comment.