-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
363 lines (318 loc) · 14.5 KB
/
index.html
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>Abie Fund</title>
<meta name="generator" content="Bootply" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="//netdna.bootstrapcdn.com/font-awesome/3.2.1/css/font-awesome.min.css" rel="stylesheet">
<!--[if lt IE 9]>
<script src="//html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link href="css/styles.css" rel="stylesheet">
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="./css/rainbow.css" rel="stylesheet" type="text/css">
<style type="text/css">
[class*="col"] { margin-bottom: 20px; }
img { width: 80%;text-align: center;}
</style>
<link rel="shortcut icon" href="cloud.ico" type="image/x-icon"/>
<link rel="icon" href="./img/sunrise_light.ico" type="image/x-icon"/>
<script src="//code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="//code.jquery.com/jquery-migrate-1.2.1.min.js"></script>
<script src="./js/rainbow.min.js"></script>
</head>
<body >
<div class="container-full">
<div class="row">
<div class="col-lg-12 text-center v-center">
<h1>Abie Fund</h1>
<p class="lead">Ethereum Social Fundraiser</p>
</div>
<div class="col-lg-2"></div>
<div class="col-lg-8 v-center">
<h3>Intro</h3>
<p>Abie Fund is a crowdfunding <a href="http://ethereum.stackexchange.com/a/384/473" target="_blank">dapp</a> built on <a href="https://ethereum.org/" target="_blank">Ethereum</a>.</p>
<p>Abie Fund is a DAO that includes a voting system based on liquid democracy (delegative model). The community vote triggers a transaction to the beneficiary. We vote when a proposal is submitted or when someone asks for membership (Sybil-proof). Liquid democracy allows participants that don't have any device or Internet access to vote on each incoming proposal, and therefore increase the participation rate.</p>
<h3>Use cases</h3>
<p><ul><li>A community that wants to fund initiatives for one specific and global cause</li>
<li>A group of people or orgs that need to raise fund in emergency</li>
<li>A brand that allows its customers to directly manage the funds allocated to certain objectives</li>
<li>A city hall that would give more decision power to people on one specific project</li></ul></p>
<p>Any community can use Abie to take collective decisions and actions in an intuitive and easy way.</p>
<h3>Features</h3>
<p><ul><li>Enable external donations (including anon)</li>
<li>Any donor can request membership</li>
<li>Anyone can submit a proposal</li>
<li>The vote of the community triggers the transaction to the beneficiary</li>
<li>Members have one right to vote each proposal (including membership request)</li>
<li>As the vote follow the rules of liquid democracy, you can choose (or become) a delegate</li>
<li>You can switch delegate or switch to direct democracy</li></ul></p>
<h3>Code</h3>
<p>The following Solidity contract was made at <a href="http://www.hackerhouse.paris/#hackerhouses" target="_blank">Hacker House Paris</a> on March 5, 2017.</p>
<p>Source: <a href="https://github.com/AbieFund/abie" target="_blank">https://github.com/AbieFund/abie</a></p>
<pre><code data-language="C">/* Part of this contract is from the solidity documentation */
pragma solidity ^0.4.8;
/// @title Fund for donations.
contract AbieFund {
uint public membershipFee = 0.1 ether;
uint public deposit = 1 ether;
uint public nbMembers;
uint public registrationTime = 1 years;
uint[2] public voteLength = [1 weeks, 1 weeks];
uint MAX_DELEGATION_DEPTH=1000;
address NOT_COUNTED=0;
address COUNTED=1;
event Donated(address donor, uint amount);
enum ProposalType {AddMember,FundProject} // Different types of proposals.
enum VoteType {Abstain,Yes,No} // Different value of a vote.
struct Proposal
{
bytes32 name; // short name (up to 32 bytes).
uint voteYes; // number of YES votes.
uint voteNo; // number of abstention. Number of No can be deduced.
address recipient; // address the funds will be sent.
uint value; // quantity of wei to be sent.
bytes32 data; // data of the transaction.
ProposalType proposalType; // type of the proposal.
uint endDate; // when the vote will be closed.
address lastMemberCounted; // last one who was counted or NOT_COUNTED (if the count has not started) or COUNTED (if all the votes has been counted);
bool executed; // True if the proposal have been executed.
mapping (address => VoteType) vote; // vote of the party.
}
// Is also a node list.
struct Member
{
uint registration; // date of registration, if 0 the member does not exist.
address[2] delegate; // delegate[proposalType] gives the delegate for the type.
address prev;
address succ; // This should not be deleted even when the member is.
uint proposalStoppedOnHim; // Number of proposals stopped on him.
}
mapping (address => Member) public members;
// Double chained list.
struct DoubleChainedList
{
address first;
address last;
}
// Chain containing all members to iterate on.
DoubleChainedList memberList;
Proposal[] public proposals;
/// Require at least price to be paid.
modifier costs(uint price) {
if (msg.value < price)
throw;
_;
}
/// Require the caller to be a member.
modifier isMember() {
if(!isValidMember(msg.sender))
throw;
_;
}
/// @param initialMembers First members of the organization.
function AbieFund(address[] initialMembers) {
for (uint i;i< initialMembers.length;++i){
Member member=members[initialMembers[i]];
member.registration=now;
if (i==0) { // initialize the list with the first member
memberList.first=initialMembers[0];
memberList.last=initialMembers[0];
} else { // add members
addMember(initialMembers[i]);
}
}
nbMembers=initialMembers.length;
}
// Add the member m to the member list.
// Assume that there is at least 1 member registrated.
function addMember(address m) private {
members[memberList.last].succ=m;
members[m].prev=memberList.last;
memberList.last=m;
}
/** Choose a delegate.
* @param proposalType 0 for AddMember, 1 for FundProject.
* @param target account to delegate to.
*/
function setDelegate(uint8 proposalType, address target)
{
members[msg.sender].delegate[proposalType] = target;
}
/// Receive funds.
function () payable {
Donated(msg.sender, msg.value);
}
/// Ask membership of the fund.
function askMembership () payable costs(membershipFee) {
Donated(msg.sender,msg.value); // Register the donation.
// Create a proposal to add the member.
proposals.push(Proposal({
name: 0x0,
voteYes: 0,
voteNo: 0,
recipient: msg.sender,
value: 0x0,
data: 0x0,
proposalType: ProposalType.AddMember,
endDate: now + voteLength[uint256(ProposalType.AddMember)],
lastMemberCounted: 0,
executed: false
}));
}
/// Add Proposal.
function addProposal (bytes32 _name, uint _value, bytes32 _data) payable costs(deposit) {
Donated(msg.sender,msg.value); // Register the donation.
// Create a proposal to add the member.
proposals.push(Proposal({
name: _name,
voteYes: 0,
voteNo: 0,
recipient: msg.sender,
value: _value,
data: _data,
proposalType: ProposalType.FundProject,
endDate: now,
lastMemberCounted: 0,
executed: false
}));
}
/** Vote for a proposal.
* @param proposalID ID of the proposal to count votes from.
* @param voteType Yes or No.
*/
function vote (uint proposalID, VoteType voteType) isMember {
Proposal proposal = proposals[proposalID];
if (proposal.vote[msg.sender] != VoteType.Abstain) // Has already voted.
throw;
if (proposal.endDate < now) // Vote is over.
throw;
proposals[proposalID].vote[msg.sender] = voteType;
}
/** Count all the votes. You can call this function if gas limit is not an issue.
* @param proposalID ID of the proposal to count votes from.
*/
function countAllVotes (uint proposalID) {
countVotes (proposalID,uint(-1));
}
/** Count up to max of the votes.
* You may have to call this function multiple times if counting once reach the gas limit.
* This function is necessary to count in multiple times if counting reach gas limit.
* We just count the number of Yes and Abstention, so we will deduce the number of No.
* @param proposalID ID of the proposal to count votes from.
* @param max maximum to count.
*/
function countVotes (uint proposalID, uint max) {
Proposal proposal = proposals[proposalID];
address current;
if (proposal.endDate > now) // You can't count while the vote is not over.
throw;
if (proposal.lastMemberCounted == COUNTED) // The count is already over
throw;
if (proposal.lastMemberCounted == NOT_COUNTED)
current = memberList.first;
else
current = proposal.lastMemberCounted;
while (max-- != 0) {
Member member=members[current];
address delegate=current;
if(isValidMember(current)) {
uint depth=0;
// Seach the final vote.
while (true){
VoteType vote=proposal.vote[delegate];
if (vote==VoteType.Abstain) { // Look at the delegate
depth+=1;
delegate=members[delegate].delegate[uint(proposal.proposalType)]; // Find the delegate.
if (delegate==current // The delegation chain forms a circle.
|| delegate==0 // Has not set a delegate.
|| depth>MAX_DELEGATION_DEPTH) { // Too much depth, we must limit it in order to avoid some circle of delegation made to consume too much gaz.
break;
}
}
if (vote==VoteType.Yes) {
proposal.voteYes+=1;
break;
} else if (vote==VoteType.No) {
proposal.voteNo+=1;
break;
}
}
} else {
// TODO: Delete the members if they are expired.
}
current=member.succ; // In next iteration start from the next node.
if (current==0) { // We reached the last member.
proposal.lastMemberCounted=COUNTED;
break;
}
}
}
function executeAddMemberProposal(uint proposalID) {
Proposal proposal = proposals[proposalID];
if (proposal.proposalType != ProposalType.AddMember) // Not a proposal to add a member.
throw;
if (!isExecutable(proposalID)) // Proposal was not approved.
throw;
proposal.executed=true; // The proposal will be executed.
addMember(proposal.recipient);
}
/// CONSTANTS ///
/** Return the delegate.
* @param member member to get the delegate from.
* @param proposalType 0 for AddMember, 1 for FundProject.
*/
function getDelegate(address member, uint8 proposalType) constant returns (address){
return members[member].delegate[proposalType];
}
/** Return true if the proposal is validated, false otherwise.
* @param proposalID ID of the proposal to count votes from.
*/
function isExecutable(uint proposalID) constant returns (bool) {
Proposal proposal = proposals[proposalID];
if (proposal.lastMemberCounted != COUNTED) // Not counted yet.
return false;
if (proposal.executed) // The proposal has already been executed.
return false;
if (proposal.value>this.balance) // Not enough to execute it.
return false;
return (proposal.voteYes>proposal.voteNo);
}
function isValidMember(address m) constant returns(bool) {
if (members[m].registration == 0) // Not a member.
return false;
if (members[m].registration+registrationTime < now) // Has expired.
return false;
return true;
}
}</code></pre>
<script src="./js/rainbow.min.js"></script>
<h3>Start</h3>
<p>Basic user would just :</p>
<p><ul><li>Deploy one contract (name, statement of intent, 1st members)</li>
<li>Create a fb group and display the address + the 1st proposal (that usually includes the types of projects to be funded)</li>
<li>Publish something every week</li>
<li>Spread the address everywhere in the crypto space</li>
<li>Reach out similar orgs so you can join forces</li></p></ul>
<h3>Next steps</h3>
<p><strong>bootstrap + jquery</strong> <i>In progress</i></p>
<p><ul><li>Create UI for 2 vital functions addProposal() and vote()</li>
<li>Deploy on mainnet and test in real --> <strong>v0.1 release</strong></li>
<li>Set a <a href="http://slack.abie.fund" target="_blank">3 ETH</a> bug bounty programme 3 ETH stored in a contract, capped at 5, 1 week vote, voting functions</li></ul></p>
<p><strong>JS+Redux+React</strong> <a href="http://slack.abie.fund" target="_blank"><i>10 ETH for this work</i></a></p>
<p><ul><li>Add membership functions triggers in UI </li>
<li>Add the setDelegate() function trigger in UI</li>
<li><strong>v1 release</strong></li></ul></p>
</br><p>Feel free to join our Slack: <a href="slack.abie.fund" target="_blank">http://slack.abie.fund</a></p>
<p>We're on <a href="https://twitter.com/AbieFund">Twitter</a> and <a href="https://www.facebook.com/abiefund">Facebook</a> too.
<br><br><br><p>Last update: July 24, 2017</p></div>
<div class="col-lg-2"></div>
</div>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.2/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
</body>
</html>