Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): DaoPriortity issues #62

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions contracts/protocol/Sector3DAO.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ contract Sector3DAO {
*/
Sector3DAOPriority[] public priorities;

event PriorityDeployed(Sector3DAOPriority priority);
developerfred marked this conversation as resolved.
Show resolved Hide resolved

constructor(string memory name_, string memory purpose_, address token_) {
name = name_;
purpose = purpose_;
token = token_;
owner = tx.origin;
owner = msg.sender;
}

/**
Expand Down Expand Up @@ -78,10 +80,11 @@ contract Sector3DAO {
token = token_;
}

function deployPriority(string calldata title, address rewardToken, uint16 epochDurationInDays, uint256 epochBudget, address gatingNFT) public returns (Sector3DAOPriority) {
function deployPriority(string calldata title, address rewardToken, uint16 epochDurationInDays, uint256 epochBudget, address gatingNFT, uint16 coolingWindowDurationInDays) public returns (Sector3DAOPriority) {
require(msg.sender == owner, "You aren't the owner");
Sector3DAOPriority priority = new Sector3DAOPriority(address(this), title, rewardToken, epochDurationInDays, epochBudget, gatingNFT);
Sector3DAOPriority priority = new Sector3DAOPriority(address(this), title, rewardToken, epochDurationInDays, epochBudget, gatingNFT, coolingWindowDurationInDays);
priorities.push(priority);
emit PriorityDeployed(priority);
return priority;
}

Expand Down
33 changes: 19 additions & 14 deletions contracts/protocol/Sector3DAOFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,45 @@ import './Sector3DAO.sol';

contract Sector3DAOFactory {

address public owner;
address public immutable owner;

address[] public daos;

event DAODeployed(address dao);

constructor() {
owner = msg.sender;
}

function setOwner(address owner_) public {
modifier onlyOwner() {
require(msg.sender == owner, "You aren't the owner");
owner = owner_;
_;
}

function getDAOs() public view returns (address[] memory) {
return daos;
}

function deployDAO(string calldata name, string calldata purpose, address token) public returns (address) {
function deployDAO(string calldata name, string calldata purpose, address token) public returns (address) {
Sector3DAO dao = new Sector3DAO(name, purpose, token);
daos.push(address(dao));
return address(dao);
}

function removeDAO(address dao) public {
require(msg.sender == owner, "You aren't the owner");
address[] memory daosAfterRemoval = new address[](daos.length - 1);
uint16 daosIndex = 0;
for (uint16 i = 0; i < daosAfterRemoval.length; i++) {
if (dao == daos[daosIndex]) {
daosIndex++;
function removeDAO(address dao) public onlyOwner {
uint256 indexToRemove = daos.length;

for (uint256 i = 0; i < daos.length; i++) {
if (dao == daos[i]) {
indexToRemove = i;
break;
}
daosAfterRemoval[i] = daos[daosIndex];
daosIndex++;
}
daos = daosAfterRemoval;

require(indexToRemove < daos.length, "DAO not found");

// Use the "delete-and-swap" technique to save gas.
daos[indexToRemove] = daos[daos.length - 1];
daos.pop();
}
}
78 changes: 77 additions & 1 deletion contracts/protocol/Sector3DAOPriority.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import "./Structs.sol";
contract Sector3DAOPriority is IPriority {
using SafeERC20 for IERC20;


struct Rating {
uint16 contributionIndex;
address rater;
uint8 rating;
}

address public immutable dao;
string public title;
IERC20 public immutable rewardToken;
Expand All @@ -17,26 +24,32 @@ contract Sector3DAOPriority is IPriority {
uint256 public immutable epochBudget;
IERC721 public immutable gatingNFT;
Contribution[] contributions;
Rating[] ratings;
mapping(uint16 => mapping(address => bool)) claims;
uint256 public claimsBalance;
uint16 public immutable coolingWindowDuration;

event ContributionAdded(Contribution contribution);
event RewardClaimed(uint16 epochIndex, address contributor, uint256 amount);
event ContributionRated(uint16 contributionIndex, address rater, uint8 rating);
event RatingUpdated(uint16 contributionIndex, address rater, uint8 newRating);

error EpochNotYetEnded();
error EpochNotYetFunded();
error NoRewardForEpoch();
error RewardAlreadyClaimed();
error NoGatingNFTOwnership();
error CoolingWindowNotEnded();

constructor(address dao_, string memory title_, address rewardToken_, uint16 epochDurationInDays, uint256 epochBudget_, address gatingNFT_) {
constructor(address dao_, string memory title_, address rewardToken_, uint16 epochDurationInDays, uint256 epochBudget_, address gatingNFT_, uint16 coolingWindowDurationInDays) {
dao = dao_;
title = title_;
rewardToken = IERC20(rewardToken_);
startTime = block.timestamp;
epochDuration = epochDurationInDays;
epochBudget = epochBudget_;
gatingNFT = IERC721(gatingNFT_);
coolingWindowDuration = coolingWindowDurationInDays;
}

/**
Expand Down Expand Up @@ -101,6 +114,9 @@ contract Sector3DAOPriority is IPriority {
if (epochIndex >= getEpochIndex()) {
revert EpochNotYetEnded();
}
if (block.timestamp < getCoolingWindowEndTime(epochIndex)) {
revert CoolingWindowNotEnded();
}
uint256 epochReward = getEpochReward(epochIndex, msg.sender);
if (epochReward == 0) {
revert NoRewardForEpoch();
Expand Down Expand Up @@ -185,4 +201,64 @@ contract Sector3DAOPriority is IPriority {
return totalFundingReceived >= totalBudget;
}
}

function rateContribution(uint16 contributionIndex, uint8 rating) public {
require(contributionIndex < contributions.length, "Invalid contribution index");
require(rating >= 1 && rating <= 5, "Invalid rating value");

uint16 ratingIndex = findRatingIndex(contributionIndex, msg.sender);

if (ratingIndex < ratings.length) {
ratings[ratingIndex].rating = rating;
emit RatingUpdated(contributionIndex, msg.sender, rating);
} else {
Rating memory newRating = Rating({
contributionIndex: contributionIndex,
rater: msg.sender,
rating: rating
});
ratings.push(newRating);
emit ContributionRated(contributionIndex, msg.sender, rating);
}
}

function findRatingIndex(uint16 contributionIndex, address rater) internal view returns (uint16) {
for (uint16 i = 0; i < ratings.length; i++) {
if (ratings[i].contributionIndex == contributionIndex && ratings[i].rater == rater) {
return i;
}
}
return uint16(ratings.length);
}

function getContributionRating(uint16 contributionIndex) public view returns (uint8) {
require(contributionIndex < contributions.length, "Invalid contribution index");

uint16 totalRatings = 0;
uint16 sumRatings = 0;

for (uint16 i = 0; i < ratings.length; i++) {
if (ratings[i].contributionIndex == contributionIndex) {
totalRatings++;
sumRatings += ratings[i].rating;
}
}

if (totalRatings == 0) {
return 0;
} else {
return uint8(sumRatings / totalRatings);
}
}

/**
* @notice Gets the end time of the cooling window for an epoch.
* @dev This should be implemented based on the requirements of the DAO.
* @param epochIndex The index of the epoch.
*/
function getCoolingWindowEndTime(uint16 epochIndex) public view returns (uint256) {
uint256 epochEndTime = startTime + (epochIndex + 1) * epochDuration * 1 days;
uint256 localCoolingWindowDuration = 3 * 1 days;
return epochEndTime + localCoolingWindowDuration;
}
}