-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstreamtide.sol
155 lines (123 loc) · 5.04 KB
/
streamtide.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
144
145
146
147
148
149
150
151
152
153
154
155
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
struct Donation {
address receiver;
uint256 amount;
}
contract MVPCLR is Ownable {
event AdminAdded(address _admin);
event AdminRemoved(address _admin);
event BlacklistedAdded(address _blacklisted);
event BlacklistedRemoved(address _blacklisted);
event PatronAdded(address addr);
event RoundStarted(uint256 roundStart, uint256 roundId, uint256 roundDuration);
event MatchingPoolDonation(address sender, uint256 value);
event Distribute(address to, uint256 amount);
event Donate(
address origin,
address sender,
uint256 value,
address patronAddress,
uint256 roundId
);
event FailedDistribute(
address receiver,
uint256 amount
);
uint256 public roundStart;
uint256 public roundDuration;
uint256 roundId;
mapping(address => bool) public isAdmin;
mapping(address => bool) public isPatron;
mapping(address => bool) public isBlacklisted;
Donation[] public donations;
int256 index_of_last_processed_donation = -1;
function closeRound() public onlyAdmin {
roundDuration = 0;
}
function roundIsClosed() public view returns (bool) {
return roundDuration != 0 && roundStart + roundDuration <= getBlockTimestamp();
}
function startRound(uint256 _roundDuration) public onlyAdmin {
roundId = roundId +1;
require(_roundDuration < 31536000, "MVPCLR: round duration too long");
roundDuration = _roundDuration;
roundStart = getBlockTimestamp();
emit RoundStarted(roundStart, roundId, roundDuration);
}
function addAdmin(address _admin) public onlyOwner {
isAdmin[_admin] = true;
emit AdminAdded(_admin);
}
function removeAdmin(address _admin) public onlyOwner {
require(isAdmin[_admin], "Admin not found"); // check if the address is an admin
delete isAdmin[_admin];
emit AdminRemoved(_admin);
}
function getBlockTimestamp() public view returns (uint256) {
return block.timestamp;
}
function addBlacklisted(address _address) public onlyAdmin {
isBlacklisted[_address] = true;
emit BlacklistedAdded(_address);
}
function removeBlacklisted(address _address) public onlyAdmin {
require(isBlacklisted[_address], "Address not blacklisted");
delete isBlacklisted[_address];
emit BlacklistedRemoved(_address);
}
function addPatron(address payable addr) public onlyAdmin {
require(!isBlacklisted[addr], "Patron address is blacklisted");
isPatron[addr] = true;
emit PatronAdded(addr);
}
function donate(address[] memory patronAddresses, uint256[] memory amounts) public payable {
require(patronAddresses.length == amounts.length, "CLR:donate - Mismatch between number of patrons and amounts");
uint256 totalAmount = 0;
for (uint256 i = 0; i < patronAddresses.length; i++) {
address patronAddress = patronAddresses[i];
uint256 amount = amounts[i];
totalAmount += amount;
require(!isBlacklisted[_msgSender()], "Sender address is blacklisted");
require(isPatron[patronAddress], "CLR:donate - Not a valid recipient");
donations.push(Donation(patronAddress, amount));
emit Donate(tx.origin, _msgSender(), amount, patronAddress, roundId);
}
require(totalAmount <= msg.value, "CLR:donate - Total amount donated is greater than the value sent");
//transfer the donated funds to the contract
payable(address(this)).transfer(msg.value);
}
function distribute(uint256 _maxProcess) external onlyAdmin {
require(roundIsClosed(), "Round is still open");
uint256 processed = 0;
while(index_of_last_processed_donation + int256(processed) < int256(donations.length)-1 && processed < _maxProcess) {
Donation memory donation = donations[uint256(index_of_last_processed_donation+1)+processed];
bool success = payable(donation.receiver).send(donation.amount);
if (success) {
emit Distribute(donation.receiver, donation.amount);
} else {
emit FailedDistribute(donation.receiver, donation.amount);
}
processed = processed + 1;
}
index_of_last_processed_donation += int256(processed);
}
//fail safe. Should be multisig. Bring this up on the call
function withdrawFunds(uint256 amount) external onlyOwner {
require(address(this).balance >= amount, "Insufficient funds in contract");
payable(owner()).transfer(amount);
}
// receive donation for the matching pool
receive() external payable {
require(
roundStart == 0 || getBlockTimestamp() < roundStart + roundDuration,
"CLR:receive closed"
);
emit MatchingPoolDonation(_msgSender(), msg.value);
}
modifier onlyAdmin() {
require(isAdmin[msg.sender] == true, "Not an admin");
_;
}
}