-
Notifications
You must be signed in to change notification settings - Fork 0
/
contract.html
380 lines (316 loc) · 17.4 KB
/
contract.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
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="dapp-styles/dist/dapp-styles.css">
<link rel="stylesheet" type="text/css" href="dapp-styles/icons/style.css">
<link rel="stylesheet" type="text/css" href="dapp-styles/icons/simple-line-icons.css">
<link rel="stylesheet" type="text/css" href="fontawsome/font-awesome-4.6.3/css/font-awesome.css">
<style type="text/css">
#outer {height: 400px; overflow: hidden; position: relative;}
#outer[id] {display: table; position: static;}
#middle {position: absolute; top: 50%;} /* for explorer only*/
#middle[id] {display: table-cell; vertical-align: middle; width: 100%;}
#inner {position: relative; top: -50%}
</style>
<script src="https://code.jquery.com/jquery-1.12.2.min.js">
</script>
<script src="bignumber.js"></script>
<script src="globalParams.js"></script>
<script src="userinput.js"></script>
<script src="deals.js"></script>
<script src="events.js"></script>
<script src="init.js"></script>
<script src="myDeals.js"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-85156609-1', 'auto');
ga('send', 'pageview');
</script>
</head>
<body>
<header class="dapp-header dapp-overflow">
<div align="center">
<h1>Decenteralized Ether Mixer  <span style="color:red">BETA!</span></h1>
</div>
</header>
<div class="dapp-flex-content dapp-overflow">
<!-- aside -->
<aside class="dapp-aside">
<nav>
<div class="list-group">
<ul>
<li>
<a class="list-group-item" href="index.html"><i class="fa fa-home fa-fw" aria-hidden="true"></i> Home</a>
</li>
<li>
<a class="list-group-item" href="mydeals.html"><i class="fa fa-tachometer fa-fw" aria-hidden="true"></i> Mix Dashboard</a>
</li>
<li>
<a class="list-group-item" href="alldeals.html"><i class="fa fa-book fa-fw" aria-hidden="true"></i> Deals Monitor</a>
</li>
<li>
<a class="list-group-item" href="contract.html"><i class="fa fa-pencil fa-fw" aria-hidden="true"></i> The Contract</a>
</li>
<li>
<a class="list-group-item" href="about.html"><i class="fa fa-cog fa-fw" aria-hidden="true"></i> About</a>
</li>
</ul>
</div>
</nav>
</aside>
<!-- content-->
<main class="dapp-content">
<h3>The contract is deployed at:</h3>
<ul>
<li>Testnet: <a href="https://testnet.etherscan.io/address/0xc6465918d6659d6a67863fe826cf04776df51ac3">0xc6465918d6659D6A67863fe826cf04776Df51aC3</a></li>
<li>Live network: <a href="https://etherscan.io/address/0x9bfe61748ba9b71789de234d2bdc8fa21047d3cb">0x9bfe61748ba9b71789de234d2bdc8fa21047d3cb</a></li>
<li>Ethereum classic: <a>0xf97f84472ff349245dc09154285b644c54fd743d</a></li>
</ul>
<h3>ABI</h3>
<pre>
<code class="language-javascript">[{"constant":false,"inputs":[{"name":"dealId","type":"uint256"}],"name":"makeDeposit","outputs":[{"name":"","type":"uint8"}],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"dealId","type":"uint256"}],"name":"makeClaim","outputs":[{"name":"","type":"uint8"}],"payable":true,"type":"function"},{"constant":false,"inputs":[{"name":"dealId","type":"uint256"}],"name":"withdraw","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"_dealId","type":"uint256"}],"name":"dealStatus","outputs":[{"name":"","type":"uint256[4]"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"_depositDurationInHours","type":"uint256"},{"name":"_claimDurationInHours","type":"uint256"},{"name":"_claimUnitValueInWei","type":"uint256"},{"name":"_claimDepositInWei","type":"uint256"},{"name":"_minNumClaims","type":"uint256"}],"name":"newDeal","outputs":[{"name":"","type":"uint8"}],"payable":false,"type":"function"},{"inputs":[],"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"user","type":"address"},{"indexed":true,"name":"_dealId","type":"uint256"},{"indexed":false,"name":"_startTime","type":"uint256"},{"indexed":false,"name":"_depositDurationInHours","type":"uint256"},{"indexed":false,"name":"_claimDurationInHours","type":"uint256"},{"indexed":false,"name":"_claimUnitValueInWei","type":"uint256"},{"indexed":false,"name":"_claimDepositInWei","type":"uint256"},{"indexed":false,"name":"_minNumClaims","type":"uint256"},{"indexed":false,"name":"_success","type":"bool"},{"indexed":false,"name":"_err","type":"string"}],"name":"NewDeal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_claimer","type":"address"},{"indexed":true,"name":"_dealId","type":"uint256"},{"indexed":false,"name":"_success","type":"bool"},{"indexed":false,"name":"_err","type":"string"}],"name":"Claim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_depositor","type":"address"},{"indexed":true,"name":"_dealId","type":"uint256"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":false,"name":"_success","type":"bool"},{"indexed":false,"name":"_err","type":"string"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_withdrawer","type":"address"},{"indexed":true,"name":"_dealId","type":"uint256"},{"indexed":false,"name":"_value","type":"uint256"},{"indexed":false,"name":"_public","type":"bool"},{"indexed":false,"name":"_success","type":"bool"},{"indexed":false,"name":"_err","type":"string"}],"name":"Withdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_dealId","type":"uint256"}],"name":"EnoughClaims","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_dealId","type":"uint256"}],"name":"DealFullyFunded","type":"event"}]
</code></pre>
<h3>Code</h3>
<pre><code class="language-javascript">pragma solidity ^0.4.2;
pragma solidity ^0.4.2;
//This project is beta stage and might contain unknown bugs.
//I am not responsible for any consequences of any use of the code or protocol that is suggested here.
contract SimpleMixer {
struct Deal{
mapping(address=>uint) deposit;
uint depositSum;
mapping(address=>bool) claims;
uint numClaims;
uint claimSum;
uint startTime;
uint depositDurationInSec;
uint claimDurationInSec;
uint claimDepositInWei;
uint claimValueInWei;
uint minNumClaims;
bool active;
bool fullyFunded;
}
Deal[] _deals;
event NewDeal( address indexed user, uint indexed _dealId, uint _startTime, uint _depositDurationInHours, uint _claimDurationInHours, uint _claimUnitValueInWei, uint _claimDepositInWei, uint _minNumClaims, bool _success, string _err );
event Claim( address indexed _claimer, uint indexed _dealId, bool _success, string _err );
event Deposit( address indexed _depositor, uint indexed _dealId, uint _value, bool _success, string _err );
event Withdraw( address indexed _withdrawer, uint indexed _dealId, uint _value, bool _public, bool _success, string _err );
event EnoughClaims( uint indexed _dealId );
event DealFullyFunded( uint indexed _dealId );
enum ReturnValue { Ok, Error }
function SimpleMixer(){
}
function newDeal( uint _depositDurationInHours, uint _claimDurationInHours, uint _claimUnitValueInWei, uint _claimDepositInWei, uint _minNumClaims ) returns(ReturnValue){
uint dealId = _deals.length;
if( _depositDurationInHours == 0 || _claimDurationInHours == 0 ){
NewDeal( msg.sender,
dealId,
now,
_depositDurationInHours,
_claimDurationInHours,
_claimUnitValueInWei,
_claimDepositInWei,
_minNumClaims,
false,
"_depositDurationInHours and _claimDurationInHours must be positive" );
return ReturnValue.Error;
}
_deals.length++;
_deals[dealId].depositSum = 0;
_deals[dealId].numClaims = 0;
_deals[dealId].claimSum = 0;
_deals[dealId].startTime = now;
_deals[dealId].depositDurationInSec = _depositDurationInHours * 1 hours;
_deals[dealId].claimDurationInSec = _claimDurationInHours * 1 hours;
_deals[dealId].claimDepositInWei = _claimDepositInWei;
_deals[dealId].claimValueInWei = _claimUnitValueInWei;
_deals[dealId].minNumClaims = _minNumClaims;
_deals[dealId].fullyFunded = false;
_deals[dealId].active = true;
NewDeal( msg.sender,
dealId,
now,
_depositDurationInHours,
_claimDurationInHours,
_claimUnitValueInWei,
_claimDepositInWei,
_minNumClaims,
true,
"all good" );
return ReturnValue.Ok;
}
function makeClaim( uint dealId ) payable returns(ReturnValue){
Deal deal = _deals[dealId];
bool errorDetected = false;
string memory error;
// validations
if( !_deals[dealId].active ){
error = "deal is not active";
errorDetected = true;
}
if( deal.startTime + deal.claimDurationInSec < now ){
error = "claim phase already ended";
errorDetected = true;
}
if( msg.value != deal.claimDepositInWei ){
error = "msg.value must be equal to claim deposit unit";
errorDetected = true;
}
if( deal.claims[msg.sender] ){
error = "cannot claim twice with the same address";
errorDetected = true;
}
if( errorDetected ){
Claim( msg.sender, dealId, false, error );
if( ! msg.sender.send(msg.value) ) throw; // send money back
return ReturnValue.Error;
}
// actual claim
deal.claimSum += deal.claimValueInWei;
deal.claims[msg.sender] = true;
deal.numClaims++;
Claim( msg.sender, dealId, true, "all good" );
if( deal.numClaims == deal.minNumClaims ) EnoughClaims( dealId );
return ReturnValue.Ok;
}
function makeDeposit( uint dealId ) payable returns(ReturnValue){
bool errorDetected = false;
string memory error;
// validations
if( msg.value == 0 ){
error = "deposit value must be positive";
errorDetected = true;
}
if( !_deals[dealId].active ){
error = "deal is not active";
errorDetected = true;
}
Deal deal = _deals[dealId];
if( deal.startTime + deal.claimDurationInSec > now ){
error = "contract is still in claim phase";
//ErrorLog( msg.sender, dealId, "makeDeposit: contract is still in claim phase");
errorDetected = true;
}
if( deal.startTime + deal.claimDurationInSec + deal.depositDurationInSec < now ){
error = "deposit phase is over";
errorDetected = true;
}
if( ( msg.value % deal.claimValueInWei ) > 0 ){
error = "deposit value must be a multiple of claim value";
//ErrorLog( msg.sender, dealId, "makeDeposit: deposit value must be a multiple of claim value");
errorDetected = true;
}
if( deal.deposit[msg.sender] > 0 ){
error = "cannot deposit twice with the same address";
//ErrorLog( msg.sender, dealId, "makeDeposit: cannot deposit twice with the same address");
errorDetected = true;
}
if( deal.numClaims < deal.minNumClaims ){
error = "deal is off as there are not enough claims. Call withdraw with you claimer address";
errorDetected = true;
}
if( errorDetected ){
Deposit( msg.sender, dealId, msg.value, false, error );
if( ! msg.sender.send(msg.value) ) throw; // send money back
return ReturnValue.Error;
}
// actual deposit
deal.depositSum += msg.value;
deal.deposit[msg.sender] = msg.value;
if( deal.depositSum >= deal.claimSum ){
deal.fullyFunded = true;
DealFullyFunded( dealId );
}
Deposit( msg.sender, dealId, msg.value, true, "all good" );
return ReturnValue.Ok;
}
function withdraw( uint dealId ) returns(ReturnValue){
// validation
bool errorDetected = false;
string memory error;
Deal deal = _deals[dealId];
bool enoughClaims = deal.numClaims >= deal.minNumClaims;
if( ! enoughClaims ){
if( deal.startTime + deal.claimDurationInSec > now ){
error = "claim phase not over yet";
//ErrorLog( msg.sender, dealId, "withdraw: claim phase not over yet");
errorDetected = true;
}
}
else{
if( deal.startTime + deal.depositDurationInSec + deal.claimDurationInSec > now ){
error = "deposit phase not over yet";
//ErrorLog( msg.sender, dealId, "withdraw: deposit phase not over yet");
errorDetected = true;
}
}
if( errorDetected ){
Withdraw( msg.sender, dealId, 0, false, false, error );
return ReturnValue.Error; // note that function is not payable
}
// actual withdraw
bool publicWithdraw;
uint withdrawedValue = 0;
if( (! deal.fullyFunded) && enoughClaims ){
publicWithdraw = true;
uint depositValue = deal.deposit[msg.sender];
if( depositValue == 0 ){
Withdraw( msg.sender, dealId, 0, publicWithdraw, false, "address made no deposit. Note that this should be called with the public address" );
//ErrorLog( msg.sender, dealId, "withdraw: address made no deposit. Note that this should be called with the public address");
return ReturnValue.Error; // function non payable
}
uint effectiveNumDeposits = deal.depositSum / deal.claimValueInWei;
uint userEffectiveNumDeposits = depositValue / deal.claimValueInWei;
uint extraBalance = ( deal.numClaims - effectiveNumDeposits ) * deal.claimDepositInWei;
uint userExtraBalance = userEffectiveNumDeposits * extraBalance / effectiveNumDeposits;
deal.deposit[msg.sender] = 0; // invalidate user
// give only half of extra balance. otherwise dishonest party could obtain 99% of the extra balance and lose almost nothing
withdrawedValue = depositValue + deal.claimDepositInWei * userEffectiveNumDeposits + ( userExtraBalance / 2 );
if( ! msg.sender.send(withdrawedValue) ) throw;
}
else{
publicWithdraw = false;
if( ! deal.claims[msg.sender] ){
Withdraw( msg.sender, dealId, 0, publicWithdraw, false, "address made no claims. Note that this should be called with the secret address" );
//ErrorLog( msg.sender, dealId, "withdraw: address made no claims. Note that this should be called with the secret address");
return ReturnValue.Error; // function non payable
}
if( enoughClaims ) withdrawedValue = deal.claimDepositInWei + deal.claimValueInWei;
else withdrawedValue = deal.claimDepositInWei;
deal.claims[msg.sender] = false; // invalidate claim
if( ! msg.sender.send(withdrawedValue) ) throw;
}
Withdraw( msg.sender, dealId, withdrawedValue, publicWithdraw, true, "all good" );
return ReturnValue.Ok;
}
////////////////////////////////////////////////////////////////////////////////////////
function dealStatus(uint _dealId) constant returns(uint[4]){
// returns (active, num claims, claim sum, deposit sum) all as integers
uint active = _deals[_dealId].active ? 1 : 0;
uint numClaims = _deals[_dealId].numClaims;
uint claimSum = _deals[_dealId].claimSum;
uint depositSum = _deals[_dealId].depositSum;
return [active, numClaims, claimSum, depositSum];
}
}
</code></pre>
</main>
<!-- actionbar -->
<aside class="dapp-actionbar dapp-overflow" hidden>
</aside>
</div>
<!--
<script type="text/javascript">
function autorun(){
init();
}
if (document.addEventListener) document.addEventListener("DOMContentLoaded", autorun, false);
else if (document.attachEvent) document.attachEvent("onreadystatechange", autorun);
else window.onload = autorun;
</script>-->
</body>
</html>