-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGovernance.sol
143 lines (114 loc) · 4.48 KB
/
Governance.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import "@openzeppelin/contracts/utils/Address.sol";
interface ITreasury {
function sendPayment(address receiver, uint amount) external;
}
interface IDAOToken {
function balanceOf(address account) external returns (uint);
function totalSupply() external returns (uint);
function snapshot() external returns (uint256 lastSnapshotId);
function getBalanceAtSnapshot(
address account,
uint256 snapshotID
) external view returns (uint256);
function getTotalSupplyAtSnapshot(
uint256 snapshotID
) external view returns (uint256);
}
contract Governance {
using Address for address;
struct Investment {
uint256 proposedAt;
uint256 snapshotID;
address startup;
uint256 amount;
uint256 votes;
bool processed;
}
uint256 private immutable VOTING_PERIOD = 3 days;
IDAOToken public token;
ITreasury public treasury;
mapping(uint256 => Investment) public investments;
// Payment ID => Voter => Voted?
mapping(uint256 => mapping(address => bool)) public voted;
uint256 private investmentsCounter;
event InvestmentWasSuggested(
uint256 investmentId,
address indexed proposer
);
event InvestmentExecuted(uint256 investmentId, address indexed executer);
event InvestmentRejected(uint256 investmentId, address indexed executer);
modifier isAllowed(address account) {
uint256 balance = token.balanceOf(account);
require(balance > 0, "can't participate");
_;
}
constructor(address _tokenAddress, address _treasuryAddress) {
require(_tokenAddress != address(0), "zero address not allowed");
require(_treasuryAddress != address(0), "zero address not allowed");
token = IDAOToken(_tokenAddress);
treasury = ITreasury(_treasuryAddress);
investmentsCounter = 1;
}
function suggestInvestment(
address _startup,
uint256 _amount
) external isAllowed(msg.sender) returns (uint256) {
require(
_startup != address(this) || _startup != address(0),
"Wrong receiver"
);
uint256 investmentId = investmentsCounter;
uint256 snapshotID = token.snapshot();
Investment storage investmentToQueue = investments[investmentId];
investmentToQueue.proposedAt = block.timestamp;
investmentToQueue.snapshotID = snapshotID;
investmentToQueue.startup = _startup;
investmentToQueue.amount = _amount;
investmentToQueue.votes = token.getBalanceAtSnapshot(
msg.sender,
snapshotID
);
investmentToQueue.processed = false;
// Mark voted for the one who suggested this investment
voted[investmentId][msg.sender] = true;
investmentsCounter++;
emit InvestmentWasSuggested(investmentId, msg.sender);
return investmentId;
}
function voteForInvestment(uint investmentId) external {
Investment storage investment = investments[investmentId];
require(investment.startup != address(0), "Investment does not exist");
require(!investment.processed, "Investment already processed");
require(!voted[investmentId][msg.sender], "Already voted");
voted[investmentId][msg.sender] = true;
investment.votes += token.getBalanceAtSnapshot(
msg.sender,
investment.snapshotID
);
}
function executeInvestment(uint256 investmentId) external {
Investment storage investment = investments[investmentId];
// Checks
require(investment.startup != address(0), "Investment does not exist");
require(!investment.processed, "Investment already processed");
uint quarterTotalSupply = token.getTotalSupplyAtSnapshot(
investment.snapshotID
) / 4;
// Enough votes
if (investment.votes > quarterTotalSupply) {
investment.processed = true;
treasury.sendPayment(investment.startup, investment.amount);
emit InvestmentExecuted(investmentId, msg.sender);
} else {
// Voting time is over, didn't get enough votes
if (block.timestamp >= investment.proposedAt + VOTING_PERIOD) {
investment.processed = true;
emit InvestmentRejected(investmentId, msg.sender);
} else {
revert("Not enough votes, voting is not over");
}
}
}
}