Skip to content

Commit

Permalink
fix sft logical errors
Browse files Browse the repository at this point in the history
  • Loading branch information
newtmex committed Aug 12, 2024
1 parent af1c7e9 commit 4861bd5
Show file tree
Hide file tree
Showing 16 changed files with 418 additions and 213 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ contract HousingProject is RentsModule, Ownable {
uint256 amountRaised,
address housingTokenAddr
) external onlyOwner {
require(amountRaised == 0, "Token details set already");
require(address(housingToken) == address(0), "Token details set already");

housingToken = ERC20Burnable(housingTokenAddr);

Expand Down
2 changes: 1 addition & 1 deletion packages/backend/contracts/housing-project/HousingSFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ contract HousingSFT is SFT {
) public view returns (HousingAttributes memory) {
require(
hasSFT(owner, nonce),
"HouisingSFT: No tokens found for user at nonce"
"HousingSFT: No tokens found for user at nonce"
);

return abi.decode(getRawTokenAttributes(nonce), (HousingAttributes));
Expand Down
63 changes: 33 additions & 30 deletions packages/backend/contracts/housing-project/RentsModule.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,81 +20,86 @@ abstract contract RentsModule is CallsSmartHousing {
uint256 public rewardsReserve;
uint256 public facilityManagementFunds;

ERC20Burnable housingToken;
ERC20Burnable public housingToken;
HousingSFT public projectSFT;

uint256 private constant REWARD_PERCENT = 75;
uint256 private constant ECOSYSTEM_PERCENT = 18;
uint256 private constant FACILITY_PERCENT = 7;

/// @notice Receives rent payments and distributes rewards.
/// @param rentPayment The details of the rent payment.
function receiveRent(ERC20TokenPayment calldata rentPayment) external {
// TODO set the appropriate rent per Project
require(
rentPayment.amount > 0,
"RentsModule: Insufficient rent amount"
);
uint256 rentAmount = rentPayment.amount;
require(rentAmount > 0, "RentsModule: Insufficient rent amount");
require(
rentPayment.token == housingToken,
"RentsModule: Invalid rent payment token"
);
rentPayment.receiveERC20();

uint256 rentReward = (rentPayment.amount * 75) / 100;
uint256 ecosystemReward = (rentPayment.amount * 18) / 100;
uint256 facilityReward = (rentPayment.amount * 7) / 100;
rentPayment.receiveERC20();

uint256 allShares = projectSFT.getMaxSupply();
uint256 rpsIncrease = (rentReward * DIVISION_SAFETY_CONST) / allShares;
uint256 rentReward = (rentAmount * REWARD_PERCENT) / 100;
uint256 ecosystemReward = (rentAmount * ECOSYSTEM_PERCENT) / 100;
uint256 facilityReward = (rentAmount * FACILITY_PERCENT) / 100;

rewardPerShare += rpsIncrease;
rewardPerShare +=
(rentReward * DIVISION_SAFETY_CONST) /
projectSFT.getMaxSupply();
rewardsReserve += rentReward;
facilityManagementFunds += facilityReward;

housingToken.burn(ecosystemReward);
ISmartHousing(smartHousingAddr).addProjectRent(rentPayment.amount);
ISmartHousing(smartHousingAddr).addProjectRent(rentAmount);
}

/// @notice Claims rent rewards for a given token.
/// @return The updated HousingAttributes.
/// @return attr The updated HousingAttributes.
function claimRentReward(
uint256 nonce
) external returns (HousingAttributes memory, rewardshares memory) {
)
external
returns (
HousingAttributes memory attr,
rewardshares memory rewardShares,
uint256 newNonce
)
{
address caller = msg.sender;
uint256 currentRPS = rewardPerShare;

HousingAttributes memory attr = projectSFT.getUserSFT(caller, nonce);
rewardshares memory rewardShares = computeRewardShares(attr);
uint256 totalReward = rewardShares.total();
attr = projectSFT.getUserSFT(caller, nonce);
rewardShares = computeRewardShares(attr);

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

require(rewardsReserve >= totalReward, "Computed rewards too large");

rewardsReserve -= totalReward;

// We use original owner since we are certain they are registered
(, address referrer) = getReferrer(attr.originalOwner);
if (rewardShares.referrerValue > 0) {
if (referrer != address(0)) {
housingToken.transfer(referrer, rewardShares.referrerValue); // Send to referrer
housingToken.transfer(referrer, rewardShares.referrerValue);
} else {
housingToken.burn(rewardShares.referrerValue); // Burn to add to ecosystem reward
housingToken.burn(rewardShares.referrerValue);
}
}

attr.rewardsPerShare = currentRPS;

projectSFT.update(
newNonce = projectSFT.update(
caller,
nonce,
projectSFT.balanceOf(caller, nonce),
abi.encode(attr)
);

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

return (attr, rewardShares);
return (attr, rewardShares, newNonce);
}

/// @notice Computes the amount of rent claimable for a given token.
Expand All @@ -113,13 +118,11 @@ abstract contract RentsModule is CallsSmartHousing {
HousingAttributes memory attr
) internal view returns (rewardshares memory) {
uint256 currentRPS = rewardPerShare;

if (currentRPS == 0 || attr.rewardsPerShare >= currentRPS) {
return rewardshares({ userValue: 0, referrerValue: 0 });
}

uint256 reward = computeReward(attr, currentRPS);

return splitReward(reward);
}
}
47 changes: 34 additions & 13 deletions packages/backend/contracts/main/SmartHousing.sol
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,18 @@ contract SmartHousing is ISmartHousing, Ownable, UserModule, ERC1155Holder {
/// @param projectAddress The address of the new project.
function addProject(address projectAddress) external onlyProjectFunding {
_setPermissions(projectAddress, Permissions.HOUSING_PROJECT);
_projectsToken.add(
address(HousingProject(projectAddress).projectSFT())
); // Register the project's SFT address
HousingProject project = HousingProject(projectAddress);
address projectSFTaddress = address(
HousingProject(projectAddress).projectSFT()
);

_projectsToken.add(projectSFTaddress); // Register the project's SFT address

distributionStorage.addProject(
projectAddress,
projectSFTaddress,
project.getMaxSupply()
);
}

/// @notice Adds rent to a project and updates the distribution storage.
Expand Down Expand Up @@ -185,22 +194,22 @@ contract SmartHousing is ISmartHousing, Ownable, UserModule, ERC1155Holder {
hstAttr.shtRewardPerShare < distributionStorage.shtRewardPerShare;
}

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

uint256 callerHstBal = hst.balanceOf(caller, hstTokenId);
uint256 callerHstBal = hst.balanceOf(caller, hstNonce);

require(callerHstBal > 0, "Caller does not own the hst token");

distributionStorage.generateRewards(epochsAndPeriodsStorage);

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

Expand All @@ -211,17 +220,29 @@ contract SmartHousing is ISmartHousing, Ownable, UserModule, ERC1155Holder {
projectToken.token != address(0),
"Invalid project address"
);
address projectAddress = distributionStorage
.projectSftToProjectAddress[projectToken.token];

// Call the external contract's claimRentReward function
(, rewardshares memory rewardShares) = HousingProject(
projectToken.token
).claimRentReward(projectToken.nonce);
(
,
rewardshares memory rewardShares,
uint256 newNonce
) = HousingProject(projectAddress).claimRentReward(
projectToken.nonce
);
hstAttr.projectTokens[i].nonce = newNonce;

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

// Update the attributes in the hst token
hst.update(caller, hstTokenId, callerHstBal, abi.encode(hstAttr));
newHstNonce = hst.update(
caller,
hstNonce,
callerHstBal,
abi.encode(hstAttr)
);

ERC20Burnable shtToken = ERC20Burnable(shtTokenAddress);

Expand Down
29 changes: 26 additions & 3 deletions packages/backend/contracts/main/distribution/Storage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ library Distribution {
uint256 genesisEpoch;
uint256 projectsTotalReceivedRents;
mapping(address => ProjectDistributionData) projectDets;
mapping(address => address) projectSftToProjectAddress;
uint256 lastFundsDispatchEpoch;
uint256 shtTotalStakeWeight;
uint256 shtRewardPerShare;
Expand Down Expand Up @@ -112,6 +113,16 @@ library Distribution {
projectData.receivedRents += amount;
}

function addProject(
Storage storage self,
address projectAddress,
address projectSFTaddress,
uint256 maxShares
) internal {
self.projectDets[projectAddress].maxShares = maxShares;
self.projectSftToProjectAddress[projectSFTaddress] = projectAddress;
}

/// @notice Generates rewards for the current epoch.
/// @param self The storage struct for the `Distribution` contract.
function generateRewards(
Expand Down Expand Up @@ -188,7 +199,8 @@ library Distribution {
// Claim SHT rewards
uint256 shtRPS = self.shtRewardPerShare;
if (shtRPS > 0 && attr.shtRewardPerShare < shtRPS) {
uint256 shtReward = (shtRPS.sub(attr.shtRewardPerShare))
uint256 shtReward = shtRPS
.sub(attr.shtRewardPerShare)
.mul(attr.stakeWeight)
.div(DIVISION_SAFETY_CONST);
if (self.shtStakingRewards < shtReward) {
Expand Down Expand Up @@ -218,13 +230,24 @@ library Distribution {
uint256 stakingCheckPoint,
uint256 tokenCheckPoint
) internal view returns (uint256 reward) {
if (stakingCheckPoint >= tokenCheckPoint) {
if (
stakingCheckPoint >= tokenCheckPoint ||
self.projectsTotalReceivedRents <= 0
) {
return 0;
}

ProjectDistributionData storage projectData = self.projectDets[
address projectAddress = self.projectSftToProjectAddress[
tokenPayment.token
];
require(
projectAddress != address(0),
"Project Address for token not set"
);

ProjectDistributionData storage projectData = self.projectDets[
self.projectSftToProjectAddress[tokenPayment.token]
];
require(
tokenPayment.amount <= projectData.maxShares,
"Project token amount too large"
Expand Down
5 changes: 3 additions & 2 deletions packages/backend/contracts/modules/SFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,14 +83,15 @@ abstract contract SFT is ERC1155, Ownable {
}

/// Burns all the NFT balance of user at nonce, creates new with balance and attributes
/// Returns new nonce
function update(
address user,
uint256 nonce,
uint256 amount,
bytes memory attr
) external onlyOwner {
) external onlyOwner returns (uint256) {
_burn(user, nonce, amount);
_mint(user, amount, attr, "");
return _mint(user, amount, attr, "");
}

function _sftBalance(
Expand Down
Loading

0 comments on commit 4861bd5

Please sign in to comment.