Created
February 15, 2022 18:42
-
-
Save justaguywhocodes/70a4d913fb1f503ded77f62701b25ead to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// SPDX-License-Identifier: MIT | |
// contracts/FundingDAO.sol | |
pragma solidity ^0.8.0; | |
import "@openzeppelin/contracts/access/AccessControl.sol"; | |
import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; | |
contract FundingDAO is ReentrancyGuard, AccessControl { | |
bytes32 public constant MEMBER = keccak256("MEMBER"); | |
bytes32 public constant STAKEHOLDER = keccak256("STAKEHOLDER"); | |
uint32 constant votingPeriod = 3 days; | |
uint256 public proposalsCount = 0; | |
struct Proposal { | |
uint256 id; | |
uint256 amount; | |
uint256 livePeriod; | |
uint256 voteInFavor; | |
uint256 voteAgainst; | |
string title; | |
string desc; | |
bool isCompleted; | |
bool paid; | |
bool isPaid; | |
address payable receiverAddress; | |
address proposer; | |
uint256 totalFundsRaised; | |
Funding[] funders; | |
string imageId; | |
} | |
struct Funding { | |
address payer; | |
uint256 amount; | |
uint256 timestamp; | |
} | |
mapping(uint256 => Proposal) private proposals; | |
mapping(address => uint256) private stakeholders; | |
mapping(address => uint256) private members; | |
mapping(address => uint256[]) private votes; | |
event NewMember( | |
address indexed fromAddress, | |
uint256 amount | |
); | |
event NewProposal( | |
address indexed proposer, | |
uint256 amount | |
); | |
event Payment( | |
address indexed stakeholder, | |
address indexed projectAddress, | |
uint256 amount | |
); | |
modifier onlyMember(string memory message) { | |
require(hasRole(MEMBER, msg.sender), message); | |
_; | |
} | |
modifier onlyStakeholder(string memory message) { | |
require(hasRole(STAKEHOLDER, msg.sender), message); | |
_; | |
} | |
function createProposal( | |
string calldata title, | |
string calldata desc, | |
address receiverAddress, | |
uint256 amount, | |
string calldata imageId | |
) public payable | |
onlyMember("Only members can create new proposals.") | |
{ | |
require( | |
msg.value == 5 * 10**18, | |
"You need to add 5 MATIC to create a proposal" | |
); | |
uint256 proposalId = proposalsCount; | |
Proposal storage proposal = proposals[proposalId]; | |
proposal.id = proposalId; | |
proposal.desc = desc; | |
proposal.title = title; | |
proposal.receiverAddress = payable(receiverAddress); | |
proposal.proposer = payable(msg.sender); | |
proposal.amount = amount; | |
proposal.livePeriod = block.timestamp + votingPeriod; | |
proposal.isPaid = false; | |
proposal.isCompleted = false; | |
proposal.imageId = imageId; | |
proposalsCount++; | |
emit NewProposal(msg.sender, amount); | |
} | |
function getAllProposals() public view returns (Proposal[] memory) { | |
Proposal[] memory tempProposals = new Proposal[](proposalsCount); | |
for (uint256 idx = 0; idx < proposalsCount; idx++) { | |
tempProposals[idx] = proposals[idx]; | |
} | |
return tempProposals; | |
} | |
function getProposal( | |
uint256 proposalId | |
) public view | |
returns (Proposal memory) { | |
return proposals[proposalId]; | |
} | |
function getVotes( | |
) public view onlyStakeholder("Stakeholders only can call this function") returns (uint256[] memory){ | |
return votes[msg.sender]; | |
} | |
function getStakeholderBal() public view onlyStakeholder("Stakeholders only can call this function") returns (uint256) { | |
return stakeholders[msg.sender]; | |
} | |
function getMemberBal() public view | |
onlyMember("Only members can call this function") | |
returns (uint256) { | |
return members[msg.sender]; | |
} | |
function isStakeholder() public view returns (bool) { | |
return stakeholders[msg.sender] > 0; | |
} | |
function isMember() public view returns (bool) { | |
return members[msg.sender] > 0; | |
} | |
function vote( | |
uint256 proposalId, | |
bool inFavour | |
) public onlyStakeholder("Only stakeholders can vote on proposals.") { | |
Proposal storage proposal = proposals[proposalId]; | |
if (proposal.isCompleted || proposal.livePeriod <= block.timestamp) { | |
proposal.isCompleted = true; | |
revert("The voting period for this proposal has concluded."); | |
} | |
for (uint256 idx = 0; idx < votes[msg.sender].length; idx++) { | |
if (proposal.id == votes[msg.sender][idx]) | |
revert("You can only vote once."); | |
} | |
if (inFavour) proposal.voteInFavor++; | |
else proposal.voteAgainst++; | |
votes[msg.sender].push(proposalId); | |
} | |
function provideFunds(uint256 proposalId, uint256 fundAmount) | |
public payable | |
onlyStakeholder("Only stakeholders can make payments."){ | |
Proposal storage proposal = proposals[proposalId]; | |
if (proposal.isPaid) revert("Required funds have already been provided."); | |
if (proposal.voteInFavor <= proposal.voteAgainst) | |
revert("This proposal is not qualified for funding."); | |
if (proposal.totalFundsRaised > proposal.amount) | |
revert("Required funds have already been provided."); | |
proposal.totalFundsRaised += fundAmount; | |
proposal.funders.push(Funding(msg.sender, fundAmount, block.timestamp)); | |
if (proposal.totalFundsRaised >= proposal.amount) { | |
proposal.isCompleted = true; | |
} | |
} | |
function releaseFunding(uint256 proposalId) public payable | |
onlyStakeholder("Only stakeholders can release funding.") | |
{ | |
Proposal storage proposal = proposals[proposalId]; | |
if (proposal.totalFundsRaised <= proposal.amount) { | |
revert("Required funds are still outstanding. Please supply needed funds."); | |
} | |
proposal.receiverAddress.transfer(proposal.totalFundsRaised); | |
proposal.isPaid = true; | |
proposal.isCompleted = true; | |
} | |
function createStakeholder() public payable { | |
uint256 amount = msg.value; | |
if (!hasRole(STAKEHOLDER, msg.sender)) { | |
uint256 total = members[msg.sender] + amount; | |
if (total >= 2 ether) { | |
_setupRole(STAKEHOLDER, msg.sender); | |
_setupRole(MEMBER, msg.sender); | |
stakeholders[msg.sender] = total; | |
members[msg.sender] += amount; | |
} else { | |
_setupRole(MEMBER, msg.sender); | |
members[msg.sender] += amount; | |
} | |
} else { | |
members[msg.sender] += amount; | |
stakeholders[msg.sender] += amount; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment