Skip to content

dex.blue Smart Contract source code

Please read the Introduction first.

For coverage of the smart contract methods useful for trading, please refer to the Smart Contract Methods page.

Verify the Smart Contract on Etherscan.

Contract ABI

1
[{"constant":false,"inputs":[{"name":"state","type":"bool"}],"name":"setMarketActiveState","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getFeeCollector","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"collector","type":"address"}],"name":"nominateFeeCollector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"feeWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"depositToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"nonce","type":"uint64"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"receiving_address","type":"address"}],"name":"multiSigTransfer","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"delegate","type":"address"}],"name":"delegateAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[]"},{"components":[{"name":"buy_token","type":"uint8"},{"name":"sell_token","type":"uint8"},{"name":"buy_amount","type":"uint256"},{"name":"sell_amount","type":"uint256"},{"name":"nonce","type":"uint64"},{"name":"v","type":"int8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"orders","type":"tuple[]"},{"components":[{"name":"maker_order","type":"uint8"},{"name":"taker_order","type":"uint8"},{"name":"maker_amount","type":"uint256"},{"name":"taker_amount","type":"uint256"},{"name":"maker_fee","type":"uint256"},{"name":"taker_fee","type":"uint256"},{"name":"maker_rebate","type":"uint256"}],"name":"trades","type":"tuple[]"}],"name":"matchTrades","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"nonce","type":"uint64"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"},{"name":"receiving_address","type":"address"}],"name":"multiSigSend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"orderHashes","type":"bytes32[]"}],"name":"orderBatchCancel","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"arbiter","type":"address"},{"name":"status","type":"bool"}],"name":"nominateArbiter","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"lockFeeCollector","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"user","type":"address"}],"name":"getLastBlockedTimestamp","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHashes","type":"bytes32[]"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"multiSigOrderBatchCancel","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"blockFundsForSingleSigWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"depositEther","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"nonce","type":"uint64"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"multiSigWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"}],"name":"initiateSingleSigWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"amount","type":"uint256"},{"name":"fee","type":"uint256"},{"name":"nonce","type":"uint64"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"userSigWithdrawal","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"token","type":"address"},{"name":"holder","type":"address"}],"name":"getBalance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"delegate","type":"address"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"revokeDelegation","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"token","type":"address"},{"name":"holder","type":"address"}],"name":"getBlocked","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"index","type":"uint8"}],"name":"TradeSettled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"index","type":"uint8"}],"name":"TradeFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"},{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"},{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"Withdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"},{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"BlockedForSingleSigWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"account","type":"address"},{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"SingleSigWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"token","type":"address"},{"indexed":false,"name":"amount","type":"uint256"}],"name":"FeeWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"hash","type":"bytes32"}],"name":"OrderCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"delegator","type":"address"},{"indexed":false,"name":"delegate","type":"address"},{"indexed":false,"name":"status","type":"bool"}],"name":"DelegateStatus","type":"event"}]

Source Code

  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
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
pragma solidity 0.4.25;
pragma experimental ABIEncoderV2;

contract dexBlue{

    // Events

    /** @notice The event, emitted when a trade is settled
      * @param  index Implying the index of the settled trade in the trade array passed to matchTrades() 
      */
    event TradeSettled(uint8 index);

    /** @notice The event, emitted when a trade settlement failed
      * @param  index Implying the index of the failed trade in the trade array passed to matchTrades() 
      */
    event TradeFailed(uint8 index);

    /** @notice The event, emitted after a successful deposit of ETH or token
      * @param  account  The address, which initiated the deposit
      * @param  token    The address of the deposited token (ETH is address(0))
      * @param  amount   The amount deposited in this transaction 
      */
    event Deposit(address account, address token, uint256 amount);

    /** @notice The event, emitted after a successful (multi-sig) withdrawal of deposited ETH or token
      * @param  account  The address, which initiated the withdrawal
      * @param  token    The address of the token which is withdrawn (ETH is address(0))
      * @param  amount   The amount withdrawn in this transaction 
      */
    event Withdrawal(address account, address token, uint256 amount);

    /** @notice The event, emitted after a user successfully blocked tokens or ETH for a single signature withdrawal
      * @param  account  The address controlling the tokens
      * @param  token    The address of the token which is blocked (ETH is address(0))
      * @param  amount   The amount blocked in this transaction 
      */
    event BlockedForSingleSigWithdrawal(address account, address token, uint256 amount);

    /** @notice The event, emitted after a successful single-sig withdrawal of deposited ETH or token
      * @param  account  The address, which initiated the withdrawal
      * @param  token    The address of the token which is withdrawn (ETH is address(0))
      * @param  amount   The amount withdrawn in this transaction 
      */
    event SingleSigWithdrawal(address account, address token, uint256 amount);

    /** @notice The event, emitted once the feeCollector address initiated a withdrawal of collected tokens or ETH via feeWithdrawal()
      * @param  token    The address of the token which is withdrawn (ETH is address(0))
      * @param  amount   The amount withdrawn in this transaction 
      */
    event FeeWithdrawal(address token, uint256 amount);

    /** @notice The event, emitted once an on-chain cancellation of an order was performed
      * @param  hash    The invalidated orders hash 
      */
    event OrderCanceled(bytes32 hash);

    /** @notice The event, emitted once a address delegation or dedelegation was performed
      * @param  delegator The delegating address,
      * @param  delegate  The delegated address,
      * @param  status    Whether the transaction delegated an address (true) or inactivated an active delegation (false) 
      */
    event DelegateStatus(address delegator, address delegate, bool status);


    // Mappings 

    mapping(address => mapping(address => uint256)) balances;                           // Users balances (token address > user address > balance amount) (ETH is address(0))
    mapping(address => mapping(address => uint256)) blocked_for_single_sig_withdrawal;  // Users balances they blocked to withdraw without arbiters multi-sig (token address > user address > balance amount) (ETH is address(0))
    mapping(address => uint256) last_blocked_timestamp;                                 // The last timestamp a user blocked tokens to withdraw without arbiters multi-sig
    mapping(bytes32 => bool) processed_withdrawals;                                     // Processed withdrawal hashes
    mapping(bytes32 => uint256) matched;                                                // Orders matched sell_amounts to prevent multiple-/over- matches of the same orders
    mapping(address => address) delegates;                                              // Delegated order signing addresses


    // EIP712 (signTypedData)

    // EIP712 Domain
    struct EIP712_Domain {
        string  name;
        string  version;
        uint256 chainId;
        address verifyingContract;
    }
    bytes32 constant EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
    bytes32          EIP712_DOMAIN_SEPARATOR;
    // Order typehash
    bytes32 constant EIP712_ORDER_TYPEHASH = keccak256("Order(address buyTokenAddress,address sellTokenAddress,uint256 buyTokenAmount,uint256 sellTokenAmount,uint64 nonce)");
    // Withdrawal typehash
    bytes32 constant EIP712_WITHDRAWAL_TYPEHASH = keccak256("Withdrawal(address token,uint256 amount,uint64 nonce)");


    // Utility functions:

    /** @notice Get the balance of a user for a specific token
      * @param  token  The token address (ETH is token address(0))
      * @param  holder The address holding the token
      * @return The amount of the specified token held by the user 
      */
    function getBalance(address token, address holder) constant public returns(uint256){
        return balances[token][holder];
    }

    /** @notice Get the balance a user blocked for a single-signature withdrawal (ETH is token address(0))
      * @param  token  The token address (ETH is token address(0))
      * @param  holder The address holding the token
      * @return The amount of the specified token blocked by the user 
      */
    function getBlocked(address token, address holder) constant public returns(uint256){
        return blocked_for_single_sig_withdrawal[token][holder];
    }

    /** @notice Returns the timestamp of the last blocked balance
      * @param  user  Address of the user which blocked funds
      * @return The last unix timestamp the user blocked funds at, which starts the waiting period for single-sig withdrawals 
      */
    function getLastBlockedTimestamp(address user) constant public returns(uint256){
        return last_blocked_timestamp[user];
    }


    // Deposit functions:

    /** @notice Deposit Ether into the smart contract 
      */
    function depositEther() public payable{
        balances[address(0)][msg.sender] += msg.value;      // Add the received ETH to the users balance
        emit Deposit(msg.sender, address(0), msg.value);    // Emit a deposit event
    }

    /** @notice Fallback function to credit ETH sent to the contract without data 
      */
    function() public payable{
        depositEther();                                     // Call the deposit function to credit ETH sent in this transaction
    }

    /** @notice Deposit ERC20 tokens into the smart contract (remember to set allowance in the token contract first)
      * @param  token   The address of the token to deposit
      * @param  amount  The amount of tokens to deposit 
      */
    function depositToken(address token, uint256 amount) public {
        Token(token).transferFrom(msg.sender, address(this), amount);    // Deposit ERC20
        require(
            checkERC20TransferSuccess(),                                 // Check whether the ERC20 token transfer was successful
            "ERC20 token transfer failed."
        );
        balances[token][msg.sender] += amount;                           // Credit the deposited token to users balance
        emit Deposit(msg.sender, token, amount);                         // Emit a deposit event
    }

    // Multi-sig withdrawal functions:

    /** @notice User-submitted withdrawal with arbiters signature, which withdraws to the users address
      * @param  token   The token to withdraw (ETH is address(address(0)))
      * @param  amount  The amount of tokens to withdraw
      * @param  nonce   The nonce (to salt the hash)
      * @param  v       Multi-signature v
      * @param  r       Multi-signature r
      * @param  s       Multi-signature s 
      */
    function multiSigWithdrawal(address token, uint256 amount, uint64 nonce, uint8 v, bytes32 r, bytes32 s) public {
        bytes32 hash = keccak256(abi.encodePacked(                      // Calculate the withdrawal hash from the parameters
            "\x19Ethereum Signed Message:\n32", 
            keccak256(abi.encodePacked(
                msg.sender,
                token,
                amount,
                nonce,
                address(this)
            ))
        ));
        if(
            !processed_withdrawals[hash]                                // Check if the withdrawal was initiated before
            && arbiters[ecrecover(hash, v,r,s)]                         // Check if the multi-sig is valid
            && balances[token][msg.sender] >= amount                    // Check if the user holds the required balance
        ){
            processed_withdrawals[hash]  = true;                        // Mark this withdrawal as processed
            balances[token][msg.sender] -= amount;                      // Substract withdrawn token from users balance
            if(token == address(0)){                                    // Withdraw ETH
                require(
                    msg.sender.send(amount),
                    "Sending of ETH failed."
                );
            }else{                                                      // Withdraw an ERC20 token
                Token(token).transfer(msg.sender, amount);              // Transfer the ERC20 token
                require(
                    checkERC20TransferSuccess(),                        // Check whether the ERC20 token transfer was successful
                    "ERC20 token transfer failed."
                );
            }

            blocked_for_single_sig_withdrawal[token][msg.sender] = 0;   // Set possible previous manual blocking of these funds to 0

            emit Withdrawal(msg.sender,token,amount);                   // Emit a Withdrawal event
        }else{
            revert();                                                   // Revert the transaction if checks fail
        }
    }    

    /** @notice User-submitted withdrawal with arbiters signature, which sends tokens to specified address
      * @param  token              The token to withdraw (ETH is address(address(0)))
      * @param  amount             The amount of tokens to withdraw
      * @param  nonce              The nonce (to salt the hash)
      * @param  v                  Multi-signature v
      * @param  r                  Multi-signature r
      * @param  s                  Multi-signature s
      * @param  receiving_address  The address to send the withdrawn token/ETH to
      */
    function multiSigSend(address token, uint256 amount, uint64 nonce, uint8 v, bytes32 r, bytes32 s, address receiving_address) public {
        bytes32 hash = keccak256(abi.encodePacked(                      // Calculate the withdrawal hash from the parameters 
            "\x19Ethereum Signed Message:\n32", 
            keccak256(abi.encodePacked(
                msg.sender,
                token,
                amount,
                nonce,
                address(this)
            ))
        ));
        if(
            !processed_withdrawals[hash]                                // Check if the withdrawal was initiated before
            && arbiters[ecrecover(hash, v,r,s)]                         // Check if the multi-sig is valid
            && balances[token][msg.sender] >= amount                    // Check if the user holds the required balance
        ){
            processed_withdrawals[hash]  = true;                        // Mark this withdrawal as processed
            balances[token][msg.sender] -= amount;                      // Substract the withdrawn balance from the users balance
            if(token == address(0)){                                    // Process an ETH withdrawal
                require(
                    receiving_address.send(amount),
                    "Sending of ETH failed."
                );
            }else{                                                      // Withdraw an ERC20 token
                Token(token).transfer(receiving_address, amount);       // Transfer the ERC20 token
                require(
                    checkERC20TransferSuccess(),                        // Check whether the ERC20 token transfer was successful
                    "ERC20 token transfer failed."
                );
            }

            blocked_for_single_sig_withdrawal[token][msg.sender] = 0;   // Set possible previous manual blocking of these funds to 0

            emit Withdrawal(msg.sender,token,amount);                   // Emit a Withdrawal event
        }else{
            revert();                                                   // Revert the transaction if checks fail
        }
    }

    /** @notice User-submitted transfer with arbiters signature, which sends tokens to another addresses account in the smart contract
      * @param  token              The token to transfer (ETH is address(address(0)))
      * @param  amount             The amount of tokens to transfer
      * @param  nonce              The nonce (to salt the hash)
      * @param  v                  Multi-signature v
      * @param  r                  Multi-signature r
      * @param  s                  Multi-signature s
      * @param  receiving_address  The address to transfer the token/ETH to
      */
    function multiSigTransfer(address token, uint256 amount, uint64 nonce, uint8 v, bytes32 r, bytes32 s, address receiving_address) public {
        bytes32 hash = keccak256(abi.encodePacked(                      // Calculate the withdrawal/transfer hash from the parameters 
            "\x19Ethereum Signed Message:\n32", 
            keccak256(abi.encodePacked(
                msg.sender,
                token,
                amount,
                nonce,
                address(this)
            ))
        ));
        if(
            !processed_withdrawals[hash]                                // Check if the withdrawal was initiated before
            && arbiters[ecrecover(hash, v,r,s)]                         // Check if the multi-sig is valid
            && balances[token][msg.sender] >= amount                    // Check if the user holds the required balance
        ){
            processed_withdrawals[hash]         = true;                 // Mark this withdrawal as processed
            balances[token][msg.sender]        -= amount;               // Substract the balance from the withdrawing account
            balances[token][receiving_address] += amount;               // Add the balance to the receiving account

            blocked_for_single_sig_withdrawal[token][msg.sender] = 0;   // Set possible previous manual blocking of these funds to 0

            emit Withdrawal(msg.sender,token,amount);                   // Emit a Withdrawal event
            emit Deposit(receiving_address,token,amount);               // Emit a Deposit event
        }else{
            revert();                                                   // Revert the transaction if checks fail
        }
    }

    /** @notice Arbiter submitted withdrawal with users multi-sig to users address
      * @param  token   The token to withdraw (ETH is address(address(0)))
      * @param  amount  The amount of tokens to withdraw
      * @param  fee     The fee, covering the gas cost of the arbiter
      * @param  nonce   The nonce (to salt the hash)
      * @param  v       Multi-signature v (either 27 or 28. To identify the different signing schemes an offset of 10 is applied for EIP712)
      * @param  r       Multi-signature r
      * @param  s       Multi-signature s
      */
    function userSigWithdrawal(address token, uint256 amount, uint256 fee, uint64 nonce, uint8 v, bytes32 r, bytes32 s) public {            
        bytes32 hash;
        if(v < 30){                                                     // Standard signing scheme (personal.sign())
            hash = keccak256(abi.encodePacked(                          // Restore multi-sig hash
                "\x19Ethereum Signed Message:\n32",
                keccak256(abi.encodePacked(
                    token,
                    amount,
                    nonce,
                    address(this)
                ))
            ));
        }else{                                                          // EIP712 signing scheme
            v -= 10;                                                    // Remove offset
            hash = keccak256(abi.encodePacked(                          // Restore multi-sig hash
                "\x19\x01",
                EIP712_DOMAIN_SEPARATOR,
                keccak256(abi.encode(
                    EIP712_WITHDRAWAL_TYPEHASH,
                    token,
                    amount,
                    nonce
                ))
            ));
        }
        address account = ecrecover(hash, v, r, s);                     // Restore signing address
        if(
            !processed_withdrawals[hash]                                // Check if the withdrawal was initiated before
            && arbiters[msg.sender]                                     // Check if transaction comes from arbiter
            && fee <= amount / 50                                       // Check if fee is not too big
            && balances[token][account] >= amount                       // Check if the user holds the required tokens
        ){
            processed_withdrawals[hash]    = true;
            balances[token][account]      -= amount;
            balances[token][feeCollector] += fee;                       // Fee to cover gas costs for the withdrawal
            if(token == address(0)){                                    // Send ETH
                require(
                    account.send(amount - fee),
                    "Sending of ETH failed."
                );
            }else{
                Token(token).transfer(account, amount - fee);           // Withdraw ERC20
                require(
                    checkERC20TransferSuccess(),                        // Check if the transfer was successful
                    "ERC20 token transfer failed."
                );
            }

            blocked_for_single_sig_withdrawal[token][account] = 0;      // Set possible previous manual blocking of these funds to 0

            emit Withdrawal(account,token,amount);                      // Emit a Withdrawal event
        }else{
            revert();                                                   // Revert the transaction is checks fail
        }
    }

    // Single-sig withdrawal functions:

    /** @notice Allows user to block funds for single-sig withdrawal after 24h waiting period 
      *         (This period is necessary to ensure all trades backed by these funds will be settled.)
      * @param  token   The address of the token to block (ETH is address(address(0)))
      * @param  amount  The amount of the token to block
      */
    function blockFundsForSingleSigWithdrawal(address token, uint256 amount) public {
        if (balances[token][msg.sender] - blocked_for_single_sig_withdrawal[token][msg.sender] >= amount){  // Check if the user holds the required funds
            blocked_for_single_sig_withdrawal[token][msg.sender] += amount;         // Block funds for manual withdrawal
            last_blocked_timestamp[msg.sender] = block.timestamp;                   // Start 24h waiting period
            emit BlockedForSingleSigWithdrawal(msg.sender,token,amount);            // Emit BlockedForSingleSigWithdrawal event
        }else{
            revert();                                                               // Revert the transaction if the user does not hold the required balance
        }
    }

    /** @notice Allows user to withdraw funds previously blocked after 24h
      */
    function initiateSingleSigWithdrawal(address token, uint256 amount) public {
        if (
            balances[token][msg.sender] >= amount                                   // Check if the user holds the funds
            && blocked_for_single_sig_withdrawal[token][msg.sender] >= amount       // Check if these funds are blocked
            && last_blocked_timestamp[msg.sender] + 86400 <= block.timestamp        // Check if the one day waiting period has passed
        ){
            balances[token][msg.sender] -= amount;                                  // Substract the tokens from users balance
            blocked_for_single_sig_withdrawal[token][msg.sender] -= amount;         // Substract the tokens from users blocked balance
            if(token == address(0)){                                                // Withdraw ETH
                require(
                    msg.sender.send(amount),
                    "Sending of ETH failed."
                );
            }else{                                                                  // Withdraw ERC20 tokens
                Token(token).transfer(msg.sender, amount);                          // Transfer the ERC20 tokens
                require(
                    checkERC20TransferSuccess(),                                    // Check if the transfer was successful
                    "ERC20 token transfer failed."
                );
            }
            emit SingleSigWithdrawal(msg.sender,token,amount);                      // Emit a SingleSigWithdrawal event
        }else{
            revert();                                                               // Revert the transaction if the required checks fail
        }
    } 


    //Trade settlement structs and function

    struct OrderInput{
        uint8       buy_token;      // The token, the order signee wants to buy
        uint8       sell_token;     // The token, the order signee wants to sell
        uint256     buy_amount;     // The total amount the signee wants to buy
        uint256     sell_amount;    // The total amount the signee wants to give for the amount he wants to buy (the orders "rate" is implied by the ratio between the two amounts)
        uint64      nonce;          // Random number to give each order an individual hash and signature
        int8        v;              // Signature v (either 27 or 28)
                                    // To identify the different signing schemes an offset of 10 is applied for EIP712.
                                    // To identify whether the order was signed by a delegated signing address, the number is either positive or negative.
        bytes32     r;              // Signature r
        bytes32     s;              // Signature s
    }

    struct TradeInput{
        uint8       maker_order;    // The index of the maker order
        uint8       taker_order;    // The index of the taker order
        uint256     maker_amount;   // The amount the maker gives in return for the taker's tokens
        uint256     taker_amount;   // The amount the taker gives in return for the maker's tokens
        uint256     maker_fee;      // The trading fee of the maker + a share in the settlement (gas) cost
        uint256     taker_fee;      // The trading fee of the taker + a share in the settlement (gas) cost
        uint256     maker_rebate;   // A optional rebate for the maker (portion of takers fee) as an incentive
    }

    /** @notice Allows an arbiter to settle trades between two user-signed orders
      * @param  addresses  Array of all addresses involved in the transactions
      * @param  orders     Array of all orders involved in the transactions
      * @param  trades     Array of the trades to be settled
      */   
    function matchTrades(address[] addresses, OrderInput[] orders, TradeInput[] trades) public {
        require(arbiters[msg.sender] && marketActive);      // Check if msg.sender is an arbiter and the market is active

        //Restore signing addresses
        uint len = orders.length;                           // Length of orders array to loop through
        bytes32[]  memory hashes = new bytes32[](len);      // Array of the restored order hashes
        address[]  memory signee = new address[](len);      // Array of the restored order signees
        OrderInput memory order;                            // Memory slot to cache orders while looping (otherwise the Stack would be too deep)
        address    addressCache1;                           // Memory slot 1 to cache addresses while looping (otherwise the Stack would be too deep)
        address    addressCache2;                           // Memory slot 2 to cache addresses while looping (otherwise the Stack would be too deep)
        bool       delegated;

        for(uint8 i = 0; i < len; i++){                     // Loop through the orders array to restore all signees
            order         = orders[i];                      // Cache order
            addressCache1 = addresses[order.buy_token];     // Cache orders buy token
            addressCache2 = addresses[order.sell_token];    // Cache orders sell token

            if(order.v < 0){                                // Check if the order is signed by a delegate
                delegated = true;                           
                order.v  *= -1;                             // Restore the negated v
            }else{
                delegated = false;
            }

            if(order.v < 30){                               // Order is hashed after signature scheme personal.sign()
                hashes[i] = keccak256(abi.encodePacked(     // Restore the hash of this order
                    "\x19Ethereum Signed Message:\n32",
                    keccak256(abi.encodePacked(
                        addressCache1,
                        addressCache2,
                        order.buy_amount,
                        order.sell_amount,
                        order.nonce,        
                        address(this)                       // This contract's address
                    ))
                ));
            }else{                                          // Order is hashed after EIP712
                order.v -= 10;                              // Remove signature format identifying offset
                hashes[i] = keccak256(abi.encodePacked(
                    "\x19\x01",
                    EIP712_DOMAIN_SEPARATOR,
                    keccak256(abi.encode(
                        EIP712_ORDER_TYPEHASH,
                        addressCache1,
                        addressCache2,
                        order.buy_amount,
                        order.sell_amount,
                        order.nonce
                    ))
                ));
            }
            signee[i] = ecrecover(                          // Restore the signee of this order
                hashes[i],                                  // Order hash
                uint8(order.v),                             // Signature v
                order.r,                                    // Signature r
                order.s                                     // Signature s
            );
            // When the signature was delegated restore delegating address
            if(delegated){
                signee[i] = delegates[signee[i]];
            }
        }

        // Settle Trades after check
        len = trades.length;                                            // Length of the trades array to loop through
        TradeInput memory trade;                                        // Memory slot to cache trades while looping
        uint maker_index;                                               // Memory slot to cache the trade's maker order index
        uint taker_index;                                               // Memory slot to cache the trade's taker order index

        for(i = 0; i < len; i++){                                       // Loop through trades to settle after checks
            trade = trades[i];                                          // Cache trade
            maker_index = trade.maker_order;                            // Cache maker order index
            taker_index = trade.taker_order;                            // Cache taker order index
            addressCache1 = addresses[orders[maker_index].buy_token];   // Cache first of the two swapped token addresses
            addressCache2 = addresses[orders[taker_index].buy_token];   // Cache second of the two swapped token addresses

            if( // Check if the arbiter has matched following the conditions of the two order signees
                // Do maker and taker want to trade the same tokens with each other
                    orders[maker_index].buy_token == orders[taker_index].sell_token
                && orders[taker_index].buy_token == orders[maker_index].sell_token

                // Do maker and taker hold the required balances
                && balances[addressCache2][signee[maker_index]] >= trade.maker_amount - trade.maker_rebate
                && balances[addressCache1][signee[taker_index]] >= trade.taker_amount

                // Are they both matched at a rate better or equal to the one they signed
                && trade.maker_amount - trade.maker_rebate <= orders[maker_index].sell_amount * trade.taker_amount / orders[maker_index].buy_amount + 1  // Check maker doesn't overpay (+ 1 to deal with rouding errors for very smal amounts)
                && trade.taker_amount <= orders[taker_index].sell_amount * trade.maker_amount / orders[taker_index].buy_amount + 1                       // Check taker doesn't overpay (+ 1 to deal with rouding errors for very smal amounts)

                // Check if the matched amount + previously matched trades doesn't exceed the amount specified by the order signee
                && trade.taker_amount + matched[hashes[taker_index]] <= orders[taker_index].sell_amount
                && trade.maker_amount - trade.maker_rebate + matched[hashes[maker_index]] <= orders[maker_index].sell_amount

                // Check if the charged fee is not too high
                && trade.maker_fee <= trade.taker_amount / 100
                && trade.taker_fee <= trade.maker_amount / 50

                // Check if maker_rebate is smaller than or equal to the taker's fee which compensates it
                && trade.maker_rebate <= trade.taker_fee
            ){
                // Settle the trade:

                // Substract sold amounts
                balances[addressCache2][signee[maker_index]] -= trade.maker_amount - trade.maker_rebate;    // Substract maker's sold amount minus the makers rebate
                balances[addressCache1][signee[taker_index]] -= trade.taker_amount;                         // Substract taker's sold amount

                // Add bought amounts
                balances[addressCache1][signee[maker_index]] += trade.taker_amount - trade.maker_fee;       // Give the maker his bought amount minus the fee
                balances[addressCache2][signee[taker_index]] += trade.maker_amount - trade.taker_fee;       // Give the taker his bought amount minus the fee

                // Save bought amounts to prevent double matching
                matched[hashes[maker_index]] += trade.maker_amount;                                         // Prevent maker order from being reused
                matched[hashes[taker_index]] += trade.taker_amount;                                         // Prevent taker order from being reused

                // Give fee to feeCollector
                balances[addressCache2][feeCollector] += trade.taker_fee - trade.maker_rebate;              // Give the feeColletor the taker fee minus the maker rebate 
                balances[addressCache1][feeCollector] += trade.maker_fee;                                   // Give the feeColletor the maker fee

                // Set possible previous manual blocking of these funds to 0
                blocked_for_single_sig_withdrawal[addressCache2][signee[maker_index]] = 0;                  // If the maker tried to block funds which he/she used in this order we have to unblock them
                blocked_for_single_sig_withdrawal[addressCache1][signee[taker_index]] = 0;                  // If the taker tried to block funds which he/she used in this order we have to unblock them

                emit TradeSettled(i);                                                                       // Emit tradeSettled Event to confirm the trade was settled
            }else{
                emit TradeFailed(i);                                                                        // Emit tradeFailed Event because the trade checks failed
            }
        }
    }


    // Order cancellation functions

    /** @notice Give the user the option to perform multiple on-chain cancellations of orders at once with arbiters multi-sig
      * @param  orderHashes Array of orderHashes of the orders to be canceled
      * @param  v           Multi-sig v
      * @param  r           Multi-sig r
      * @param  s           Multi-sig s
      */
    function multiSigOrderBatchCancel(bytes32[] orderHashes, uint8 v, bytes32 r, bytes32 s) public {
        if(
            arbiters[                                               // Check if the signee is an arbiter
                ecrecover(                                          // Restore the signing address
                    keccak256(abi.encodePacked(                     // Restore the signed hash (hash of all orderHashes)
                        "\x19Ethereum Signed Message:\n32", 
                        keccak256(abi.encodePacked(orderHashes))
                    )),
                    v, r, s
                )
            ]
        ){
            uint len = orderHashes.length;
            for(uint8 i = 0; i < len; i++){
                matched[orderHashes[i]] = 2**256 - 1;               // Set the matched amount of all orders to the maximum
                emit OrderCanceled(orderHashes[i]);                 // emit OrderCanceled event
            }
        }else{
            revert();
        }
    }

    /** @notice Give arbiters the option to perform on-chain multiple cancellations of orders at once  
      * @param orderHashes Array of hashes of the orders to be canceled
      */
    function orderBatchCancel(bytes32[] orderHashes) public {
        if(
            arbiters[msg.sender]                        // Check if the sender is an arbiter
        ){
            uint len = orderHashes.length;
            for(uint8 i = 0; i < len; i++){
                matched[orderHashes[i]] = 2**256 - 1;   // Set the matched amount of all orders to the maximum
                emit OrderCanceled(orderHashes[i]);     // emit OrderCanceled event
            }
        }else{
            revert();
        }
    }


    // Signature delegation

    /** @notice delegate an address to allow it to sign orders on your behalf
      * @param delegate  The address to delegate
      */
    function delegateAddress(address delegate) public {
        // set as delegate
        require(delegates[delegate] == address(0), "Address is already a delegate");
        delegates[delegate] = msg.sender;

        emit DelegateStatus(msg.sender, delegate, true);
    }

    /** @notice revoke the delegation of an address
      * @param  delegate  The delegated address
      * @param  v         Multi-sig v
      * @param  r         Multi-sig r
      * @param  s         Multi-sig s
      */
    function revokeDelegation(address delegate, uint8 v, bytes32 r, bytes32 s) public {
        bytes32 hash = keccak256(abi.encodePacked(              // Restore the signed hash
            "\x19Ethereum Signed Message:\n32", 
            keccak256(abi.encodePacked(
                delegate,
                msg.sender,
                address(this)
            ))
        ));

        require(arbiters[ecrecover(hash, v, r, s)], "MultiSig is not from known arbiter");  // Check if signee is an arbiter

        delegates[delegate] = address(1);       // set to 1 not 0 to prevent double delegation, which would make old signed order valid for the new delegator

        emit DelegateStatus(msg.sender, delegate, false);
    }


    // Management functions:

    address owner;                      // Contract owner address (has the right to nominate arbiters and the feeCollectors addresses)   
    address feeCollector;               // feeCollector address
    bool marketActive = true;           // Make it possible to pause the market
    bool feeCollectorLocked = false;    // Make it possible to lock the feeCollector address (to allow to change the feeCollector to a fee distribution contract)
    mapping(address => bool) arbiters;  // Mapping of arbiters

    /** @notice Constructor function
      */
    constructor() public {
        owner = msg.sender;             // Nominate sender to be the contract owner
        feeCollector = msg.sender;      // Nominate sender to be the standart feeCollector
        arbiters[msg.sender] = true;    // Nominate sender to be an arbiter

        // create EIP712 domain seperator
        EIP712_Domain memory eip712Domain = EIP712_Domain({
            name              : "dex.blue",
            version           : "1",
            chainId           : 1,
            verifyingContract : this
        });
        EIP712_DOMAIN_SEPARATOR = keccak256(abi.encode(
            EIP712_DOMAIN_TYPEHASH,
            keccak256(bytes(eip712Domain.name)),
            keccak256(bytes(eip712Domain.version)),
            eip712Domain.chainId,
            eip712Domain.verifyingContract
        ));
    }

    /** @notice Allows the owner to nominate or denominate trade arbitting addresses
      * @param  arbiter The arbiter whose status to change
      * @param  status  Whether the address should be an arbiter (true) or not (false)
      */
    function nominateArbiter(address arbiter, bool status) public {
        require(msg.sender == owner);                           // Check if sender is owner
        arbiters[arbiter] = status;                             // Update address status
    }

    /** @notice Allows the owner to pause / unpause the market
      * @param  state  Whether the the market should be active (true) or paused (false)
      */
    function setMarketActiveState(bool state) public {
        require(msg.sender == owner);                           // Check if sender is owner
        marketActive = state;                                   // pause / unpause market
    }

    /** @notice Allows the owner to nominate the feeCollector address
      * @param  collector The address to nominate as feeCollector
      */
    function nominateFeeCollector(address collector) public {
        require(msg.sender == owner && !feeCollectorLocked);    // Check if sender is owner and feeCollector address is not locked
        feeCollector = collector;                               // Update feeCollector address
    }

    /** @notice Allows the owner to lock the feeCollector address
  */
    function lockFeeCollector() public {
        require(msg.sender == owner);                           // Check if sender is owner
        feeCollectorLocked = true;                              // Lock feeCollector address
    }

    /** @notice Get the feeCollectors address
      * @return The feeCollectors address
      */
    function getFeeCollector() public constant returns (address){
        return feeCollector;
    }

    /** @notice Allows the feeCollector to directly withdraw his funds (would allow a fee distribution contract to withdraw collected fees)
      * @param  token   The token to withdraw
      * @param  amount  The amount of tokens to withdraw
  */
    function feeWithdrawal(address token, uint256 amount) public {
        if (
            msg.sender == feeCollector                              // Check if the sender is the feeCollector
            && balances[token][feeCollector] >= amount              // Check if feeCollector has the sufficient balance
        ){
            balances[token][feeCollector] -= amount;                // Substract the feeCollectors balance
            if(token == address(0)){                                // Is the withdrawal token ETH
                require(
                    feeCollector.send(amount),                      // Withdraw ETH
                    "Sending of ETH failed."
                );
            }else{
                Token(token).transfer(feeCollector, amount);        // Withdraw ERC20
                require(                                            // Revert if the withdrawal failed
                    checkERC20TransferSuccess(),
                    "ERC20 token transfer failed."
                );
            }
            emit FeeWithdrawal(token,amount);                       // Emit FeeWithdrawal event
        }else{
            revert();                                               // Revert the transaction if the checks fail
        }
    }

    // We have to check returndatasize after ERC20 tokens transfers, as some tokens are implemented badly (dont return a boolean)
    function checkERC20TransferSuccess() pure private returns(bool){
        uint256 success = 0;

        assembly {
            switch returndatasize               // Check the number of bytes the token contract returned
                case 0 {                        // Nothing returned, but contract did not throw > assume our transfer succeeded
                    success := 1
                }
                case 32 {                       // 32 bytes returned, result is the returned bool
                    returndatacopy(0, 0, 32)
                    success := mload(0)
                }
        }

        return success != 0;
    }
}




// Standart ERC20 token interface to interact with ERC20 token contracts
// To support badly implemented tokens (which dont return a boolean on the transfer functions)
// we have to expect a badly implemented token and then check with checkERC20TransferSuccess() whether the transfer succeeded

contract Token {
    /** @return total amount of tokens
      */
    function totalSupply() constant public returns (uint256 supply) {}

    /** @param _owner The address from which the balance will be retrieved
      * @return The balance
      */
    function balanceOf(address _owner) constant public returns (uint256 balance) {}

    /** @notice send `_value` token to `_to` from `msg.sender`
      * @param  _to     The address of the recipient
      * @param  _value  The amount of tokens to be transferred
      * @return Whether the transfer was successful or not
      */
    function transfer(address _to, uint256 _value) public {}

    /** @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
      * @param  _from   The address of the sender
      * @param  _to     The address of the recipient
      * @param  _value  The amount of tokens to be transferred
      * @return Whether the transfer was successful or not
      */
    function transferFrom(address _from, address _to, uint256 _value)  public {}

    /** @notice `msg.sender` approves `_addr` to spend `_value` tokens
      * @param  _spender The address of the account able to transfer the tokens
      * @param  _value   The amount of wei to be approved for transfer
      * @return Whether the approval was successful or not
      */
    function approve(address _spender, uint256 _value) public returns (bool success) {}

    /** @param  _owner   The address of the account owning tokens
      * @param  _spender The address of the account able to transfer the tokens
      * @return Amount of remaining tokens allowed to spend
      */
    function allowance(address _owner, address _spender) constant public returns (uint256 remaining) {}

    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    uint public decimals;
    string public name;
}