-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathpethreon.sol
More file actions
191 lines (159 loc) · 6.18 KB
/
Copy pathpethreon.sol
File metadata and controls
191 lines (159 loc) · 6.18 KB
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
pragma solidity 0.4.19;
/*
An Ethereum version of recurring payments.
Creator:
1. publishes address (via website, etc)
2. can withdraw a certain amount once every PERIOD
Supporter:
1. Deposits ether
2. Pledges to give N wei to Creator once a PERIOD
3. Can unsubscribe any time (pledges for earlier periods not refunded)
*/
contract Pethreon {
/***** EVENTS *****/
event SupporterDeposited(uint period, address supporter, uint amount);
event PledgeCreated(uint period, address creator, address supporter, uint weiPerPeriod, uint periods);
event PledgeCancelled(uint period, address creator, address supporter);
event SupporterWithdrew(uint period, address supporter, uint amount);
event CreatorWithdrew(uint period, address creator, uint amount);
/***** CONSTANTS *****/
// Time is processed in steps of 1 PERIOD
// Period 0 is the Start of epoch -- i.e., contract creation
uint period;
uint startOfEpoch;
/***** DATA STRUCTURES *****/
struct Pledge {
address creator;
uint weiPerPeriod;
uint afterLastPeriod; // first period s.t. pledge makes no payment
bool initialized;
}
mapping (address => uint) supporterBalances;
mapping (address => uint) creatorBalances;
// supporter => (creator => pledge)
mapping(address => mapping(address => Pledge)) pledges;
// creator => (periodNumber => payment)
mapping (address => mapping(uint => uint)) expectedPayments;
mapping (address => uint) afterLastWithdrawalPeriod;
/***** HELPER FUNCTIONS *****/
function Pethreon(uint _period) {
startOfEpoch = now;
period = _period;
}
function currentPeriod()
internal
view
returns (uint periodNumber) {
return (now - startOfEpoch) / period;
}
/*
// TODO: get expected payments in batch (can't return uint[]?)
function getExpectedPayment(uint period) constant returns (uint expectedPayment) {
return (period < afterLastWithdrawalPeriod[msg.sender]) ? 0 :
expectedPayments[msg.sender][period];
}
*/
/***** DEPOSIT & WITHDRAW *****/
// Get your (yet unpledged) balance as a supporter
function balanceAsSupporter()
public
view
returns (uint) {
return supporterBalances[msg.sender];
}
function balanceAsCreator()
public
view
returns (uint) {
// sum up all expected payments from all pledges from all previous periods
uint256 amount = 0;
for (var period = afterLastWithdrawalPeriod[msg.sender]; period < currentPeriod(); period++) {
amount += expectedPayments[msg.sender][period];
}
return amount;
}
// deposit ether to be used in future pledges
function deposit()
public
payable
returns (uint newBalance) {
supporterBalances[msg.sender] += msg.value;
SupporterDeposited(currentPeriod(), msg.sender, msg.value);
return supporterBalances[msg.sender];
}
// withdraw ether (generic function)
function withdraw(bool isSupporter, uint amount)
internal
returns (uint newBalance) {
var balances = isSupporter ? supporterBalances : creatorBalances;
uint oldBalance = balances[msg.sender];
if (balances[msg.sender] < amount) return oldBalance;
balances[msg.sender] -= amount;
if (!msg.sender.send(amount)) {
balances[msg.sender] += amount;
return oldBalance;
}
return balances[msg.sender];
}
// Supporter can choose how much to withdraw
function withdrawAsSupporter(uint amount)
public {
withdraw(true, amount);
SupporterWithdrew(currentPeriod(), msg.sender, amount);
}
// Creator can only withdraw the full amount available (keeping it simple!)
function withdrawAsCreator()
public {
var amount = balanceAsCreator();
afterLastWithdrawalPeriod[msg.sender] = currentPeriod();
withdraw(false, amount);
CreatorWithdrew(currentPeriod(), msg.sender, amount);
}
/***** PLEDGES *****/
function canPledge(uint _weiPerPeriod, uint _periods)
internal
view
returns (bool enoughFunds) {
return (supporterBalances[msg.sender] >= _weiPerPeriod * _periods);
}
function createPledge(address _creator, uint _weiPerPeriod, uint _periods)
public {
// must have enough funds
require(canPledge(_weiPerPeriod, _periods));
// can't pledge twice for same creator (for simplicity)
// to change pledge parameters, cancel it and create a new one
require(!pledges[msg.sender][_creator].initialized);
// update creator's mapping of future payments
for (uint period = currentPeriod(); period < _periods; period++) {
expectedPayments[_creator][period] += _weiPerPeriod;
}
// store the data structure so that supporter can cancel pledge
var pledge = Pledge({
creator: _creator,
weiPerPeriod: _weiPerPeriod,
afterLastPeriod: currentPeriod() + _periods,
initialized: true
});
pledges[msg.sender][_creator] = pledge;
supporterBalances[msg.sender] -= _weiPerPeriod * _periods;
PledgeCreated(currentPeriod(), _creator, msg.sender, _weiPerPeriod, _periods);
}
function cancelPledge(address _creator)
public {
var pledge = pledges[msg.sender][_creator];
require(pledge.initialized);
supporterBalances[msg.sender] += pledge.weiPerPeriod * (pledge.afterLastPeriod - currentPeriod());
for (uint period = currentPeriod(); period < pledge.afterLastPeriod; period++) {
expectedPayments[_creator][period] -= pledge.weiPerPeriod;
}
delete pledges[msg.sender][_creator];
PledgeCancelled(currentPeriod(), _creator, msg.sender);
}
function myPledgeTo(address _creator)
public
view
returns (uint weiPerPeriod, uint afterLastPeriod) {
var pledge = pledges[msg.sender][_creator];
return (pledge.weiPerPeriod, pledge.afterLastPeriod);
}
}