added HumanStandardToken to NettingChannelContract

import "HumanStandardToken.sol";

contract NettingChannelContract {
    uint public lockedTime;
    address public assetAddress;
    uint public opened;
    uint public closed;
    uint public settled;
    address public closingAddress;
    HumanStandardToken public assetToken;

    struct Participant
    {
        address addr;
        uint deposit;
        uint netted;
        uint transferedAmount;
        bytes merkleProof;
        bytes32 hashlock;
        bytes32 secret;
        uint expiration;
        address sender;
        uint nonce;
        address asset;
        uint balance;
        address recipient;
        bytes32 locksroot;
    }
    Participant[2] public participants; // We only have two participants at all times

    event ChannelOpened(address assetAdr); // TODO
    event ChannelClosed(); // TODO
    event ChannelSettled(); // TODO
    event ChannelSecretRevealed(); //TODO

    /// @dev modifier ensuring that on a participant of the channel can call a function
    modifier inParticipants {
        if (msg.sender != participants[0].addr &&
            msg.sender != participants[1].addr) throw;
        _
    }

    function NettingChannelContract(address assetAdr, address participant1, address participant2, uint lckdTime) {
        opened = 0;
        closed = 0;
        settled = 0;
        assetAddress = HumanStandardToken(assetAdr);
        participants[0].addr = participant1;
        participants[1].addr = participant2;
        lockedTime = lckdTime;
    }

    /// @notice atIndex(address) to get the index of an address (0 or 1)
    /// @dev get the index of an address
    /// @param addr (address) the address you want the index of
    function atIndex(address addr) private returns (uint index) {
        if (addr == participants[0].addr) return 0;
        else return 1;
    }

    /// @notice deposit(uint) to deposit amount to channel.
    /// @dev Deposit an amount to the channel. At least one of the participants
    /// must deposit before the channel is opened.
    /// @param amount (uint) the amount to be deposited to the address
    function deposit(uint amount) inParticipants {
        if (assetAddress.balanceOf(msg.sender) < amount) throw;
        bool s = assetAddress.transfer(this, amount);
        if (s == true) participants[atIndex(msg.sender)].deposit += amount;
        if(isOpen() && opened == 0) open();
    }

    /// @notice open() to set the opened to be the current block and triggers
    /// the event ChannelOpened()
    /// @dev Sets the value of `opened` to be the value of the current block.
    /// param none
    /// returns none, but changes the value of `opened` and triggers the event ChannelOpened.
    function open() private {
        opened = block.number;
        // trigger event
        ChannelOpened(assetAddress);
    }

    /// @notice partner() to get the partner or other participant of the channel
    /// @dev Get the other participating party of the channel
    /// @return p (address) the partner of the calling party
    function partner(address a) private returns (address p) {
        if (a == participants[0].addr) return participants[1].addr;
        else return participants[0].addr;
    }

    /// @notice isOpen() to check if a channel is open
    /// @dev Check if a channel is open and both parties have deposited to the channel
    /// @return open (bool) the status of the channel
    function isOpen() private returns (bool open) {
        if (closed == 0) throw;
        if (participants[0].deposit > 0 || participants[1].deposit > 0) return true;
        else return false;
    }

    /// @notice addrAndDep() to get the addresses and deposits of the participants
    /// @dev get the addresses and deposits of the participants
    /// @return par1 (address) address of one of the participants
    /// @return par2 (address) address of the the other participant
    /// @return dep1 (uint) the deposit of the first participant
    /// @return dep2 (uint) the deposit of the second participant
    function addrAndDep() returns (address par1, uint dep1, address par2, uint dep2) {
        par1 = participants[0].addr;
        dep1 = participants[0].deposit;
        par2 = participants[1].addr;
        dep2 = participants[1].deposit;
    }

    /// @notice close(bytes, bytes) to close a channel between to parties
    /// @dev Close the channel between two parties
    /// @param firstEncoded (bytes) the last sent transfer of the msg.sender
    function close(bytes firstEncoded) inParticipants {
        if (settled > 0) throw; // channel already settled
        if (closed > 0) throw; // channel is closing

        // check if sender of message is a participant
        if (getSender(firstEncoded) != participants[0].addr &&
            getSender(firstEncoded) != participants[1].addr) throw;

        uint partnerId = atIndex(partner(msg.sender));
        uint senderId = atIndex(msg.sender);

        decode(firstEncoded);

        // mark closed
        closed = block.number;
        closingAddress = msg.sender;

        uint amount1 = participants[senderId].transferedAmount;
        uint amount2 = participants[partnerId].transferedAmount;

        uint allowance = participants[senderId].deposit + participants[partnerId].deposit;
        uint difference;
        if(amount1 > amount2) {
            difference = amount1 - amount2;
        } else {
            difference = amount2 - amount1;
        }

        // TODO
        // if (difference > allowance) penalize();

        // trigger event
        //TODO
        ChannelClosed();
    }

    /// @notice close(bytes, bytes) to close a channel between to parties
    /// @dev Close the channel between two parties
    /// @param firstEncoded (bytes) the last sent transfer of the msg.sender
    /// @param secondEncoded (bytes) the last sent transfer of the msg.sender
    function close(bytes firstEncoded, bytes secondEncoded) inParticipants {
        if (settled > 0) throw; // channel already settled
        if (closed > 0) throw; // channel is closing

        // check if the sender of either of the messages is a participant
        if (getSender(firstEncoded) != participants[0].addr &&
            getSender(firstEncoded) != participants[1].addr) throw;
        if (getSender(secondEncoded) != participants[0].addr &&
            getSender(secondEncoded) != participants[1].addr) throw;

        // Don't allow both transfers to be from the same sender
        if (getSender(firstEncoded) == getSender(secondEncoded)) throw;

        uint partnerId = atIndex(partner(msg.sender));
        uint senderId = atIndex(msg.sender);

        decode(firstEncoded);
        decode(secondEncoded);

        // mark closed
        closed = block.number;
        closingAddress = msg.sender;

        uint amount1 = participants[senderId].transferedAmount;
        uint amount2 = participants[partnerId].transferedAmount;

        uint allowance = participants[senderId].deposit + participants[partnerId].deposit;
        uint difference;
        if(amount1 > amount2) {
            difference = amount1 - amount2;
        } else {
            difference = amount2 - amount1;
        }

        // TODO
        // if (difference > allowance) penalize();

        // trigger event
        //TODO
        ChannelClosed();
    }

    /// @notice updateTransfer(bytes) to update last known transfer
    /// @dev Allow the partner to update the last known transfer
    /// @param message (bytes) the encoded transfer message
    function updateTransfer(bytes message) inParticipants {
        if (settled > 0) throw; // channel already settled
        if (closed == 0) throw; // channel is open
        if (msg.sender == closingAddress) throw; // don't allow closer to update
        if (closingAddress == getSender(message)) throw;

        decode(message);

        // TODO check if tampered and penalize
        // TODO check if outdated and penalize

    }

    /// @notice unlock(bytes, bytes, bytes32) to unlock a locked transfer
    /// @dev Unlock a locked transfer
    /// @param lockedEncoded (bytes) the lock
    /// @param merkleProof (bytes) the merkle proof
    /// @param secret (bytes32) the secret
    function unlock(bytes lockedEncoded, bytes merkleProof, bytes32 secret) inParticipants{
        if (settled > 0) throw; // channel already settled
        if (closed == 0) throw; // channel is open

        uint partnerId = atIndex(partner(msg.sender));
        uint senderId = atIndex(msg.sender);

        if (participants[partnerId].nonce == 0) throw;

        bytes32 h = sha3(lockedEncoded);

        for (uint i = 0; i < merkleProof.length; i += 64) {
            bytes32 left;
            left = bytesToBytes32(slice(merkleProof, i, i + 32), left);
            bytes32 right;
            right = bytesToBytes32(slice(merkleProof, i + 32, i + 64), right);
            if (h != left && h != right) throw;
            h = sha3(left, right);
        }

        if (participants[partnerId].locksroot != h) throw;

        // TODO decode lockedEncoded into a Unlocked struct and append

        //participants[partnerId].unlocked.push(lock);
    }

    /// @notice settle() to settle the balance between the two parties
    /// @dev Settles the balances of the two parties fo the channel
    /// @return participants (Participant[]) the participants with netted balances
    /*
    function settle() returns (Participant[] participants) {
        if (settled > 0) throw;
        if (closed == 0) throw;
        if (closed + lockedTime > block.number) throw; //if locked time has expired throw

        for (uint i = 0; i < participants.length; i++) {
            uint otherIdx = atIndex(partner(participants[i].addr));
            participants[i].netted = participants[i].deposit;
            if (participants[i].lastSentTransfer != 0) {
                participants[i].netted = participants[i].lastSentTransfer.balance;
            }
            if (participants[otherIdx].lastSentTransfer != 0) {
                participants[i].netted = participants[otherIdx].lastSentTransfer.balance;
            }
        }

        //for (uint j = 0; j < participants.length; j++) {
            //uint otherIdx = atIndex(partner(participants[j].addr));
        //}

        // trigger event
        //ChannelSettled();
    }
    */

    // Get the nonce of the last sent transfer
    function getNonce(bytes message) private returns (uint8 non) {
        // Direct Transfer
        if (message[0] == 5) {
            (non, , , , , , ) = decodeTransfer(message);
        }
        // Locked Transfer
        if (message[0] == 6) {
            (non, , , ) = decodeLockedTransfer1(message);
        }
        // Mediated Transfer
        if (message[0] == 7) {
            (non, , , , ) = decodeMediatedTransfer1(message);
        }
        // Cancel Transfer
        if (message[0] == 8) {
            (non, , , ) = decodeCancelTransfer1(message);
        }
        else throw;
    }

    // Gets the sender of a last sent transfer
    function getSender(bytes message) private returns (address sndr) {
        // Secret
        if (message[0] == 4) {
            sndr = dsec(message);
        }
        // Direct Transfer
        if (message[0] == 5) {
            sndr = ddtran(message);
        }
        // Locked Transfer
        if (message[0] == 6) {
            sndr = dltran(message);
        }
        // Mediated Transfer
        if (message[0] == 7) {
            sndr = dmtran(message);
        }
        // Cancel Transfer
        if (message[0] == 8) {
            sndr = dctran(message);
        }
        else throw;
    }

    function dsec(bytes message) private returns (address sndr) {
        var(sec, sig) = decodeSecret(message);
        bytes32 h = sha3(sec);
        sndr = ecrecovery(h, sig);
    }
    function ddtran(bytes message) private returns (address sndr) {
        var(non, ass, rec, bal, olo, , sig) = decodeTransfer(message);
        bytes32 h = sha3(non, ass, bal, rec, olo); //need the optionalLocksroot
        sndr = ecrecovery(h, sig);
    }
    function dltran(bytes message) private returns (address sndr) {
        bytes32 lock;
        var(non, , ass, rec) = decodeLockedTransfer1(message);
        var(loc, bal, , has, sig) = decodeLockedTransfer2(message);
        bytes32 h = sha3(non, ass, bal, rec, loc, lock ); //need the lock
        sndr = ecrecovery(h, sig);
    }
    function dmtran(bytes message) private returns (address sndr) {
        bytes32 lock;
        var(non, , ass, rec, tar) = decodeMediatedTransfer1(message);
        var(ini, loc, bal, , , fee, sig) = decodeMediatedTransfer2(message);
        bytes32 h = sha3(non, ass, bal, rec, loc, lock, tar, ini, fee); //need the lock
        sndr = ecrecovery(h, sig);
    }
    function dctran(bytes message) private returns (address sndr) {
        bytes32 lock;
        var(non, , ass, rec) = decodeCancelTransfer1(message);
        var(loc, bal, , , sig) = decodeCancelTransfer2(message);
        bytes32 h = sha3(non, ass, bal, rec, loc, lock); //need the lock
        sndr = ecrecovery(h, sig);
    }

    function decode(bytes message) private {
        bytes32 hash;
        // Secret
        uint i;
        if (message[0] == 4) {
            decsec(message);
        }
        // Direct Transfer
        if (message[0] == 5) {
            decdir(message);
        }
        // Locked Transfer
        if (message[0] == 6) {
            hash = decloc1(message);
            decloc2(message, hash);
        }
        // Mediated Transfer
        if (message[0] == 7) {
            hash = decmed1(message);
            decmed2(message, hash);
        }
        // Cancel Transfer
        if (message[0] == 8) {
            hash = deccan1(message);
            deccan2(message, hash);
        }
        else throw;
    }

    function decsec(bytes message) private {
        uint i = atIndex(getSender(message));
        var(sec, sig) = decodeSecret(message);
        participants[atIndex(msg.sender)].secret = sec;
        bytes32 h = sha3(sec);
        participants[i].sender = ecrecovery(h, sig);
    }
    function decdir(bytes message) private {
        bytes32 lock;
        uint i = atIndex(getSender(message));
        var(non, ass, rec, bal, loc, sec, sig) = decodeTransfer(message);
        participants[i].nonce = non;
        participants[i].asset = ass;
        participants[i].recipient = rec;
        participants[i].balance = bal;
        bytes32 h = sha3(non, ass, bal, rec, loc);
        participants[i].sender = ecrecovery(h, sig);
    }
    function decloc1(bytes message) private returns (bytes32 hh) {
        uint i = atIndex(getSender(message));
        var(non, exp, ass, rec) = decodeLockedTransfer1(message);
        participants[i].nonce = non;
        lockedTime = exp;
        participants[i].asset = ass;
        participants[i].recipient = rec;
        hh = sha3(non, ass, rec);
    }
    function decloc2(bytes message, bytes32 hh) private {
        bytes32 lock;
        uint i = atIndex(getSender(message));
        var(loc, bal, amo, has, sig) = decodeLockedTransfer2(message);
        participants[i].locksroot = loc;
        participants[i].balance = bal;
        participants[i].transferedAmount = amo;
        participants[i].hashlock = has;
        bytes32 h = sha3(hh, bal, loc, lock ); //need the lock
        participants[i].sender = ecrecovery(h, sig);
    }
    function decmed1(bytes message) private returns (bytes32 hh) {
        uint i = atIndex(getSender(message));
        var(non, exp, ass, rec, tar) = decodeMediatedTransfer1(message);
        participants[i].nonce = non;
        lockedTime = exp;
        participants[i].asset = ass;
        participants[i].recipient = rec;
        hh = sha3(non, ass, rec, tar);
    }
    function decmed2(bytes message, bytes32 hh) private {
        bytes32 lock;
        uint i = atIndex(getSender(message));
        var(ini, loc, has, bal, amo, fee, sig) = decodeMediatedTransfer2(message);
        participants[i].locksroot = loc;
        participants[i].hashlock = has;
        participants[i].balance = bal;
        participants[i].transferedAmount = amo;
        bytes32 h = sha3(hh, bal, loc, lock, ini, fee); //need the lock
        participants[i].sender = ecrecovery(h, sig);
    }
    function deccan1(bytes message) private returns (bytes32 hh) {
        uint i = atIndex(getSender(message));
        var(non, exp, ass, rec) = decodeCancelTransfer1(message);
        participants[i].nonce = non;
        lockedTime = exp;
        participants[i].asset = ass;
        participants[i].recipient = rec;
        hh = sha3(non, ass, rec);
    }
    function deccan2(bytes message, bytes32 hh) private {
        bytes32 lock;
        uint i = atIndex(getSender(message));
        var(loc, bal, amo, has, sig) = decodeCancelTransfer2(message);
        participants[i].locksroot = loc;
        participants[i].balance = bal;
        participants[i].transferedAmount = amo;
        participants[i].hashlock = has;
        bytes32 h = sha3(hh, bal, loc, lock); //need the lock
        participants[i].sender = ecrecovery(h, sig);
    }

    // Written by Alex Beregszaszi (@axic), use it under the terms of the MIT license.
    function ecrecovery(bytes32 hash, bytes sig) private returns (address) {
        bytes32 r;
        bytes32 s;
        uint8 v;

        // FIXME: Should this throw, or return 0?
        if (sig.length != 65)
          return 0;

        // The signature format is a compact form of:
        //   {bytes32 r}{bytes32 s}{uint8 v}
        // Compact means, uint8 is not padded to 32 bytes.
        assembly {
            r := mload(add(sig, 32))
            s := mload(add(sig, 64))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(sig, 65)), 1)
        }

        // old geth sends a `v` value of [0,1], while the new, in line with the YP sends [27,28]
        if (v < 27) v += 27;

        return ecrecover(hash, v, r, s);
    }

    function ecverify(bytes32 hash, bytes sig, address signer) private returns (bool) {
        return ecrecovery(hash, sig) == signer;
    }

    function slice(bytes a, uint start, uint end) private returns (bytes n) {
        if (a.length < end) throw;
        if (start < 0) throw;
        if (start > end) throw;
        n = new bytes(end-start);
        for ( uint i = start; i < end; i ++) { //python style slice
            n[i-start] = a[i];
        }
    }

    function decodeSecret(bytes m) private returns (bytes32 secret, bytes signature) {
        if (m.length != 101) throw;
        secret = bytesToBytes32(slice(m, 4, 36), secret);
        signature = slice(m, 36, 101);
    }

    function decodeTransfer(bytes m) private
        returns
        (uint8 nonce,
        address asset,
        address recipient,
        uint balance,
        bytes32 optionalLocksroot,
        bytes32 optionalSecret,
        bytes signature)
    {
        if (m.length != 213) throw;
        nonce = bytesToIntEight(slice(m, 4, 12), nonce);
        uint160 ia;
        asset = bytesToAddress(slice(m, 12, 32), ia);
        uint160 ir;
        recipient = bytesToAddress(slice(m, 32, 52), ir);
        balance = bytesToInt(slice(m, 52, 84), balance);
        optionalLocksroot = bytesToBytes32(slice(m, 84, 116), optionalLocksroot);
        optionalSecret = bytesToBytes32(slice(m, 116, 148), optionalSecret);
        signature = slice(m, 148, 213);

    }

    function decodeLockedTransfer1(bytes m) private
        returns
        (uint8 nonce,
        uint8 expiration,
        address asset,
        address recipient)
    {
        if (m.length != 253) throw;
        nonce = bytesToIntEight(slice(m, 4, 12), nonce);
        expiration = bytesToIntEight(slice(m, 12, 20), expiration);
        uint160 ia;
        asset = bytesToAddress(slice(m, 20, 40), ia);
        uint160 ir;
        recipient = bytesToAddress(slice(m, 40, 60), ir);
    }

    function decodeLockedTransfer2(bytes m) private
        returns
        (bytes32 locksroot,
        uint balance,
        uint amount,
        bytes32 hashlock,
        bytes signature)
    {

        locksroot = bytesToBytes32(slice(m, 60, 92), locksroot);
        balance = bytesToInt(slice(m, 92, 124), balance);
        amount = bytesToInt(slice(m, 124, 156), amount);
        hashlock = bytesToBytes32(slice(m, 156, 188), hashlock);
        signature = slice(m, 188, 253);
    }

    function decodeMediatedTransfer1(bytes m) private
        returns
        (uint8 nonce,
        uint8 expiration,
        address asset,
        address recipient,
        address target)
    {
        if (m.length != 325) throw;
        nonce = bytesToIntEight(slice(m, 4, 12), nonce);
        expiration = bytesToIntEight(slice(m, 12, 20), expiration);
        uint160 ia;
        asset = bytesToAddress(slice(m, 20, 40), ia);
        uint160 ir;
        recipient = bytesToAddress(slice(m, 40, 60), ir);
        uint160 it;
        target = bytesToAddress(slice(m, 60, 80), it);
    }

    function decodeMediatedTransfer2(bytes m) private
        returns
        (address initiator,
        bytes32 locksroot,
        bytes32 hashlock,
        uint balance,
        uint amount,
        uint fee,
        bytes signature)
    {
        uint160 ii;
        initiator = bytesToAddress(slice(m, 80, 100), ii);
        locksroot = bytesToBytes32(slice(m, 100, 132), locksroot);
        hashlock = bytesToBytes32(slice(m, 132, 164), hashlock);
        balance = bytesToInt(slice(m, 164, 196), balance);
        amount = bytesToInt(slice(m, 196, 228), amount);
        fee = bytesToInt(slice(m, 228, 260), fee);
        signature = slice(m, 260, 325);
    }

    function decodeCancelTransfer1(bytes m) private
        returns
        (uint8 nonce,
        uint8 expiration,
        address asset,
        address recipient)
    {
        if (m.length != 253) throw;
        nonce = bytesToIntEight(slice(m, 4, 12), nonce);
        expiration = bytesToIntEight(slice(m, 12, 20), expiration);
        uint160 ia;
        asset = bytesToAddress(slice(m, 20, 40), ia);
        uint160 ir;
        recipient = bytesToAddress(slice(m, 40, 60), ir);
    }

    function decodeCancelTransfer2(bytes m) private
    returns
    (bytes32 locksroot,
    uint balance,
    uint amount,
    bytes32 hashlock,
    bytes signature)
    {
        locksroot = bytesToBytes32(slice(m, 60, 92), locksroot);
        balance = bytesToInt(slice(m, 92, 124), balance);
        amount = bytesToInt(slice(m, 124, 156), amount);
        hashlock = bytesToBytes32(slice(m, 156, 188), hashlock);
        signature = slice(m, 188, 253);
    }

    /* HELPER FUNCTIONS */

    function bytesToIntEight(bytes b, uint8 i) private returns (uint8 res) {
        assembly { i := mload(add(b, 0x8)) }
        res = i;
    }

    function bytesToInt(bytes b, uint i) private returns (uint res) {
        assembly { i := mload(add(b, 0x20)) }
        res = i;
    }

    function bytesToAddress(bytes b, uint160 i) private returns (address add) {
        assembly { i := mload(add(b, 0x14)) }
        uint160 a = uint160(i);
        add = address(i);
    }

    function bytesToBytes32(bytes b, bytes32 i) private returns (bytes32 bts) {
        assembly { i := mload(add(b, 0x20)) }
        bts = i;
    }

    // empty function to handle wrong calls
    function () { throw; }
}

contract NettingChannelContract {
    uint public lockedTime;
    address public assetAddress;
    uint public opened;
    uint public closed;
    uint public settled;
    address public closingAddress;

    struct Participant
    {
        address addr;
        uint deposit;
        uint netted;
        uint transferedAmount;
        bytes merkleProof;
        bytes32 hashlock;
        bytes32 secret;
        uint expiration;
        address sender;
        uint nonce;
        address asset;
        uint balance;
        address recipient;
        bytes32 locksroot;
    }
    Participant[2] public participants; // We only have two participants at all times

    event ChannelOpened(address assetAdr); // TODO
    event ChannelClosed(); // TODO
    event ChannelSettled(); // TODO
    event ChannelSecretRevealed(); //TODO

    /// @dev modifier ensuring that on a participant of the channel can call a function
    modifier inParticipants {
        if (msg.sender != participants[0].addr &&
            msg.sender != participants[1].addr) throw;
        _
    }

    function NettingChannelContract(address assetAdr, address participant1, address participant2, uint lckdTime) {
        opened = 0;
        closed = 0;
        settled = 0;
        assetAddress = assetAdr;
        participants[0].addr = participant1;
        participants[1].addr = participant2;
        lockedTime = lckdTime;
    }

    /// @notice atIndex(address) to get the index of an address (0 or 1)
    /// @dev get the index of an address
    /// @param addr (address) the address you want the index of
    function atIndex(address addr) private returns (uint index) {
        if (addr == participants[0].addr) return 0;
        else return 1;
    }

    /// @notice deposit(uint) to deposit amount to channel.
    /// @dev Deposit an amount to the channel. At least one of the participants
    /// must deposit before the channel is opened.
    /// @param amount (uint) the amount to be deposited to the address
    function deposit(uint amount) inParticipants {
        if (msg.sender.balance < amount) throw; // TODO check asset contract
        participants[atIndex(msg.sender)].deposit += amount;
        if(isOpen() && opened == 0) open();
    }

    /// @notice open() to set the opened to be the current block and triggers
    /// the event ChannelOpened()
    /// @dev Sets the value of `opened` to be the value of the current block.
    /// param none
    /// returns none, but changes the value of `opened` and triggers the event ChannelOpened.
    function open() private {
        opened = block.number;

        // trigger event
        ChannelOpened(assetAddress);
    }

    /// @notice partner() to get the partner or other participant of the channel
    /// @dev Get the other participating party of the channel
    /// @return p (address) the partner of the calling party
    function partner(address a) private returns (address p) {
        if (a == participants[0].addr) return participants[1].addr;
        else return participants[0].addr;
    }

    /// @notice isOpen() to check if a channel is open
    /// @dev Check if a channel is open and both parties have deposited to the channel
    /// @return open (bool) the status of the channel
    function isOpen() returns (bool open) {
        if (closed == 0) throw;
        if (participants[0].deposit > 0 || participants[1].deposit > 0) return true;
        else return false;
    }

    /// @notice addrAndDep() to get the addresses and deposits of the participants
    /// @dev get the addresses and deposits of the participants
    /// @return par1 (address) address of one of the participants
    /// @return par2 (address) address of the the other participant
    /// @return dep1 (uint) the deposit of the first participant
    /// @return dep2 (uint) the deposit of the second participant
    function addrAndDep() returns (address par1, uint dep1, address par2, uint dep2) {
        par1 = participants[0].addr;
        dep1 = participants[0].deposit;
        par2 = participants[1].addr;
        dep2 = participants[1].deposit;
    }

    /// @notice close(bytes, bytes) to close a channel between to parties
    /// @dev Close the channel between two parties
    /// @param firstEncoded (bytes) the last sent transfer of the msg.sender
    function close(bytes firstEncoded) inParticipants {
        if (settled > 0) throw; // channel already settled
        if (closed > 0) throw; // channel is closing

        // check if sender of message is a participant
        if (getSender(firstEncoded) != participants[0].addr &&
            getSender(firstEncoded) != participants[1].addr) throw;

        uint partnerId = atIndex(partner(msg.sender));
        uint senderId = atIndex(msg.sender);

        decode(firstEncoded);

        // mark closed
        closed = block.number;
        closingAddress = msg.sender;

        uint amount1 = participants[senderId].transferedAmount;
        uint amount2 = participants[partnerId].transferedAmount;

        uint allowance = participants[senderId].deposit + participants[partnerId].deposit;
        uint difference;
        if(amount1 > amount2) {
            difference = amount1 - amount2;
        } else {
            difference = amount2 - amount1;
        }

        // TODO
        // if (difference > allowance) penalize();

        // trigger event
        //TODO
        ChannelClosed();
    }

    /// @notice close(bytes, bytes) to close a channel between to parties
    /// @dev Close the channel between two parties
    /// @param firstEncoded (bytes) the last sent transfer of the msg.sender
    /// @param secondEncoded (bytes) the last sent transfer of the msg.sender
    function close(bytes firstEncoded, bytes secondEncoded) inParticipants {
        if (settled > 0) throw; // channel already settled
        if (closed > 0) throw; // channel is closing

        // check if the sender of either of the messages is a participant
        if (getSender(firstEncoded) != participants[0].addr &&
            getSender(firstEncoded) != participants[1].addr) throw;
        if (getSender(secondEncoded) != participants[0].addr &&
            getSender(secondEncoded) != participants[1].addr) throw;

        // Don't allow both transfers to be from the same sender
        if (getSender(firstEncoded) == getSender(secondEncoded)) throw;

        uint partnerId = atIndex(partner(msg.sender));
        uint senderId = atIndex(msg.sender);

        decode(firstEncoded);
        decode(secondEncoded);

        // mark closed
        closed = block.number;
        closingAddress = msg.sender;

        uint amount1 = participants[senderId].transferedAmount;
        uint amount2 = participants[partnerId].transferedAmount;

        uint allowance = participants[senderId].deposit + participants[partnerId].deposit;
        uint difference;
        if(amount1 > amount2) {
            difference = amount1 - amount2;
        } else {
            difference = amount2 - amount1;
        }

        // TODO
        // if (difference > allowance) penalize();

        // trigger event
        //TODO
        ChannelClosed();
    }

    /// @notice updateTransfer(bytes) to update last known transfer
    /// @dev Allow the partner to update the last known transfer
    /// @param message (bytes) the encoded transfer message
    function updateTransfer(bytes message) inParticipants {
        if (settled > 0) throw; // channel already settled
        if (closed == 0) throw; // channel is open
        if (msg.sender == closingAddress) throw; // don't allow closer to update
        if (closingAddress == getSender(message)) throw;

        decode(message);

        // TODO check if tampered and penalize
        // TODO check if outdated and penalize

    }

    /// @notice unlock(bytes, bytes, bytes32) to unlock a locked transfer
    /// @dev Unlock a locked transfer
    /// @param lockedEncoded (bytes) the lock
    /// @param merkleProof (bytes) the merkle proof
    /// @param secret (bytes32) the secret
    function unlock(bytes lockedEncoded, bytes merkleProof, bytes32 secret) inParticipants{
        if (settled > 0) throw; // channel already settled
        if (closed == 0) throw; // channel is open

        uint partnerId = atIndex(partner(msg.sender));
        uint senderId = atIndex(msg.sender);

        if (participants[partnerId].nonce == 0) throw;

        bytes32 h = sha3(lockedEncoded);

        for (uint i = 0; i < merkleProof.length; i += 64) {
            bytes32 left;
            left = bytesToBytes32(slice(merkleProof, i, i + 32), left);
            bytes32 right;
            right = bytesToBytes32(slice(merkleProof, i + 32, i + 64), right);
            if (h != left && h != right) throw;
            h = sha3(left, right);
        }

        if (participants[partnerId].locksroot != h) throw;

        // TODO decode lockedEncoded into a Unlocked struct and append

        //participants[partnerId].unlocked.push(lock);
    }

    /// @notice settle() to settle the balance between the two parties
    /// @dev Settles the balances of the two parties fo the channel
    /// @return participants (Participant[]) the participants with netted balances
    /*
    function settle() returns (Participant[] participants) {
        if (settled > 0) throw;
        if (closed == 0) throw;
        if (closed + lockedTime > block.number) throw; //if locked time has expired throw

        for (uint i = 0; i < participants.length; i++) {
            uint otherIdx = atIndex(partner(participants[i].addr));
            participants[i].netted = participants[i].deposit;
            if (participants[i].lastSentTransfer != 0) {
                participants[i].netted = participants[i].lastSentTransfer.balance;
            }
            if (participants[otherIdx].lastSentTransfer != 0) {
                participants[i].netted = participants[otherIdx].lastSentTransfer.balance;
            }
        }

        //for (uint j = 0; j < participants.length; j++) {
            //uint otherIdx = atIndex(partner(participants[j].addr));
        //}

        // trigger event
        //ChannelSettled();
    }
    */

    // Get the nonce of the last sent transfer
    function getNonce(bytes message) private returns (uint8 non) {
        // Direct Transfer
        if (message[0] == 5) {
            (non, , , , , , ) = decodeTransfer(message);
        }
        // Locked Transfer
        if (message[0] == 6) {
            (non, , , ) = decodeLockedTransfer1(message);
        }
        // Mediated Transfer
        if (message[0] == 7) {
            (non, , , , ) = decodeMediatedTransfer1(message);
        }
        // Cancel Transfer
        if (message[0] == 8) {
            (non, , , ) = decodeCancelTransfer1(message);
        }
        else throw;
    }

    // Gets the sender of a last sent transfer
    function getSender(bytes message) private returns (address sndr) {

        // Secret
        if (message[0] == 4) {
            sndr = dsec(message);
        }
        // Direct Transfer
        if (message[0] == 5) {
            sndr = ddtran(message);
        }
        // Locked Transfer
        if (message[0] == 6) {
            sndr = dltran(message);
        }
        // Mediated Transfer
        if (message[0] == 7) {
            sndr = dmtran(message);
        }
        // Cancel Transfer
        if (message[0] == 8) {
            sndr = dctran(message);
        }
        else throw;
    }

    function dsec(bytes message) private returns (address sndr) {
        var(sec, sig) = decodeSecret(message);
        bytes32 h = sha3(sec);
        sndr = ecrecovery(h, sig);
    }
    function ddtran(bytes message) private returns (address sndr) {
        var(non, ass, rec, bal, olo, , sig) = decodeTransfer(message);
        bytes32 h = sha3(non, ass, bal, rec, olo); //need the optionalLocksroot
        sndr = ecrecovery(h, sig);
    }
    function dltran(bytes message) private returns (address sndr) {
        bytes32 lock;
        var(non, , ass, rec) = decodeLockedTransfer1(message);
        var(loc, bal, , has, sig) = decodeLockedTransfer2(message);
        bytes32 h = sha3(non, ass, bal, rec, loc, lock ); //need the lock
        sndr = ecrecovery(h, sig);
    }
    function dmtran(bytes message) private returns (address sndr) {
        bytes32 lock;
        var(non, , ass, rec, tar) = decodeMediatedTransfer1(message);
        var(ini, loc, bal, , , fee, sig) = decodeMediatedTransfer2(message);
        bytes32 h = sha3(non, ass, bal, rec, loc, lock, tar, ini, fee); //need the lock
        sndr = ecrecovery(h, sig);
    }
    function dctran(bytes message) private returns (address sndr) {
        bytes32 lock;
        var(non, , ass, rec) = decodeCancelTransfer1(message);
        var(loc, bal, , , sig) = decodeCancelTransfer2(message);
        bytes32 h = sha3(non, ass, bal, rec, loc, lock); //need the lock
        sndr = ecrecovery(h, sig);
    }

    function decode(bytes message) private {
        bytes32 hash;
        // Secret
        uint i;
        if (message[0] == 4) {
            decsec(message);
        }
        // Direct Transfer
        if (message[0] == 5) {
            decdir(message);
        }
        // Locked Transfer
        if (message[0] == 6) {
            hash = decloc1(message);
            decloc2(message, hash);
        }
        // Mediated Transfer
        if (message[0] == 7) {
            hash = decmed1(message);
            decmed2(message, hash);
        }
        // Cancel Transfer
        if (message[0] == 8) {
            hash = deccan1(message);
            deccan2(message, hash);
        }
        else throw;
    }

    function decsec(bytes message) private {
        uint i = atIndex(getSender(message));
        var(sec, sig) = decodeSecret(message);
        participants[atIndex(msg.sender)].secret = sec;
        bytes32 h = sha3(sec);
        participants[i].sender = ecrecovery(h, sig);
    }
    function decdir(bytes message) private {
        bytes32 lock;
        uint i = atIndex(getSender(message));
        var(non, ass, rec, bal, loc, sec, sig) = decodeTransfer(message);
        participants[i].nonce = non;
        participants[i].asset = ass;
        participants[i].recipient = rec;
        participants[i].balance = bal;
        bytes32 h = sha3(non, ass, bal, rec, loc);
        participants[i].sender = ecrecovery(h, sig);
    }
    function decloc1(bytes message) private returns (bytes32 hh) {
        uint i = atIndex(getSender(message));
        var(non, exp, ass, rec) = decodeLockedTransfer1(message);
        participants[i].nonce = non;
        lockedTime = exp;
        participants[i].asset = ass;
        participants[i].recipient = rec;
        hh = sha3(non, ass, rec);
    }
    function decloc2(bytes message, bytes32 hh) private {
        bytes32 lock;
        uint i = atIndex(getSender(message));
        var(loc, bal, amo, has, sig) = decodeLockedTransfer2(message);
        participants[i].locksroot = loc;
        participants[i].balance = bal;
        participants[i].transferedAmount = amo;
        participants[i].hashlock = has;
        bytes32 h = sha3(hh, bal, loc, lock ); //need the lock
        participants[i].sender = ecrecovery(h, sig);
    }
    function decmed1(bytes message) private returns (bytes32 hh) {
        uint i = atIndex(getSender(message));
        var(non, exp, ass, rec, tar) = decodeMediatedTransfer1(message);
        participants[i].nonce = non;
        lockedTime = exp;
        participants[i].asset = ass;
        participants[i].recipient = rec;
        hh = sha3(non, ass, rec, tar);
    }
    function decmed2(bytes message, bytes32 hh) private {
        bytes32 lock;
        uint i = atIndex(getSender(message));
        var(ini, loc, has, bal, amo, fee, sig) = decodeMediatedTransfer2(message);
        participants[i].locksroot = loc;
        participants[i].hashlock = has;
        participants[i].balance = bal;
        participants[i].transferedAmount = amo;
        bytes32 h = sha3(hh, bal, loc, lock, ini, fee); //need the lock
        participants[i].sender = ecrecovery(h, sig);
    }
    function deccan1(bytes message) private returns (bytes32 hh) {
        uint i = atIndex(getSender(message));
        var(non, exp, ass, rec) = decodeCancelTransfer1(message);
        participants[i].nonce = non;
        lockedTime = exp;
        participants[i].asset = ass;
        participants[i].recipient = rec;
        hh = sha3(non, ass, rec);
    }
    function deccan2(bytes message, bytes32 hh) private {
        bytes32 lock;
        uint i = atIndex(getSender(message));
        var(loc, bal, amo, has, sig) = decodeCancelTransfer2(message);
        participants[i].locksroot = loc;
        participants[i].balance = bal;
        participants[i].transferedAmount = amo;
        participants[i].hashlock = has;
        bytes32 h = sha3(hh, bal, loc, lock); //need the lock
        participants[i].sender = ecrecovery(h, sig);
    }

    // Written by Alex Beregszaszi (@axic), use it under the terms of the MIT license.
    function ecrecovery(bytes32 hash, bytes sig) private returns (address) {
        bytes32 r;
        bytes32 s;
        uint8 v;

        // FIXME: Should this throw, or return 0?
        if (sig.length != 65)
          return 0;

        // The signature format is a compact form of:
        //   {bytes32 r}{bytes32 s}{uint8 v}
        // Compact means, uint8 is not padded to 32 bytes.
        assembly {
            r := mload(add(sig, 32))
            s := mload(add(sig, 64))
            // Here we are loading the last 32 bytes, including 31 bytes
            // of 's'. There is no 'mload8' to do this.
            //
            // 'byte' is not working due to the Solidity parser, so lets
            // use the second best option, 'and'
            v := and(mload(add(sig, 65)), 1)
        }

        // old geth sends a `v` value of [0,1], while the new, in line with the YP sends [27,28]
        if (v < 27)
          v += 27;

        return ecrecover(hash, v, r, s);
    }

    function ecverify(bytes32 hash, bytes sig, address signer) private returns (bool) {
        return ecrecovery(hash, sig) == signer;
    }

    function slice(bytes a, uint start, uint end) private returns (bytes n) {
        if (a.length < end) throw;
        if (start < 0) throw;
        if (start > end) throw;
        n = new bytes(end-start);
        for ( uint i = start; i < end; i ++) { //python style slice
            n[i-start] = a[i];
        }
    }

    function decodeSecret(bytes m) private returns (bytes32 secret, bytes signature) {
        if (m.length != 101) throw;
        secret = bytesToBytes32(slice(m, 4, 36), secret);
        signature = slice(m, 36, 101);
    }

    function decodeTransfer(bytes m) private
        returns
        (uint8 nonce,
        address asset,
        address recipient,
        uint balance,
        bytes32 optionalLocksroot,
        bytes32 optionalSecret,
        bytes signature)
    {
        if (m.length != 213) throw;
        nonce = bytesToIntEight(slice(m, 4, 12), nonce);
        uint160 ia;
        asset = bytesToAddress(slice(m, 12, 32), ia);
        uint160 ir;
        recipient = bytesToAddress(slice(m, 32, 52), ir);
        balance = bytesToInt(slice(m, 52, 84), balance);
        optionalLocksroot = bytesToBytes32(slice(m, 84, 116), optionalLocksroot);
        optionalSecret = bytesToBytes32(slice(m, 116, 148), optionalSecret);
        signature = slice(m, 148, 213);

    }

    function decodeLockedTransfer1(bytes m) private
        returns
        (uint8 nonce,
        uint8 expiration,
        address asset,
        address recipient)
    {
        if (m.length != 253) throw;
        nonce = bytesToIntEight(slice(m, 4, 12), nonce);
        expiration = bytesToIntEight(slice(m, 12, 20), expiration);
        uint160 ia;
        asset = bytesToAddress(slice(m, 20, 40), ia);
        uint160 ir;
        recipient = bytesToAddress(slice(m, 40, 60), ir);

    }

    function decodeLockedTransfer2(bytes m) private
        returns
        (bytes32 locksroot,
        uint balance,
        uint amount,
        bytes32 hashlock,
        bytes signature)
    {

        locksroot = bytesToBytes32(slice(m, 60, 92), locksroot);
        balance = bytesToInt(slice(m, 92, 124), balance);
        amount = bytesToInt(slice(m, 124, 156), amount);
        hashlock = bytesToBytes32(slice(m, 156, 188), hashlock);
        signature = slice(m, 188, 253);
    }

    function decodeMediatedTransfer1(bytes m) private
        returns
        (uint8 nonce,
        uint8 expiration,
        address asset,
        address recipient,
        address target)
    {
        if (m.length != 325) throw;
        nonce = bytesToIntEight(slice(m, 4, 12), nonce);
        expiration = bytesToIntEight(slice(m, 12, 20), expiration);
        uint160 ia;
        asset = bytesToAddress(slice(m, 20, 40), ia);
        uint160 ir;
        recipient = bytesToAddress(slice(m, 40, 60), ir);
        uint160 it;
        target = bytesToAddress(slice(m, 60, 80), it);

    }

    function decodeMediatedTransfer2(bytes m) private
        returns
        (address initiator,
        bytes32 locksroot,
        bytes32 hashlock,
        uint balance,
        uint amount,
        uint fee,
        bytes signature)
    {
        uint160 ii;
        initiator = bytesToAddress(slice(m, 80, 100), ii);
        locksroot = bytesToBytes32(slice(m, 100, 132), locksroot);
        hashlock = bytesToBytes32(slice(m, 132, 164), hashlock);
        balance = bytesToInt(slice(m, 164, 196), balance);
        amount = bytesToInt(slice(m, 196, 228), amount);
        fee = bytesToInt(slice(m, 228, 260), fee);
        signature = slice(m, 260, 325);
    }

    function decodeCancelTransfer1(bytes m) private
        returns
        (uint8 nonce,
        uint8 expiration,
        address asset,
        address recipient)
    {
        if (m.length != 253) throw;
        nonce = bytesToIntEight(slice(m, 4, 12), nonce);
        expiration = bytesToIntEight(slice(m, 12, 20), expiration);
        uint160 ia;
        asset = bytesToAddress(slice(m, 20, 40), ia);
        uint160 ir;
        recipient = bytesToAddress(slice(m, 40, 60), ir);
    }

    function decodeCancelTransfer2(bytes m) private
    returns
    (bytes32 locksroot,
    uint balance,
    uint amount,
    bytes32 hashlock,
    bytes signature)
    {
        locksroot = bytesToBytes32(slice(m, 60, 92), locksroot);
        balance = bytesToInt(slice(m, 92, 124), balance);
        amount = bytesToInt(slice(m, 124, 156), amount);
        hashlock = bytesToBytes32(slice(m, 156, 188), hashlock);
        signature = slice(m, 188, 253);
    }

    /* HELPER FUNCTIONS */

    function bytesToIntEight(bytes b, uint8 i) private returns (uint8 res) {
        assembly { i := mload(add(b, 0x8)) }
        res = i;
    }

    function bytesToInt(bytes b, uint i) private returns (uint res) {
        assembly { i := mload(add(b, 0x20)) }
        res = i;
    }

    function bytesToAddress(bytes b, uint160 i) private returns (address add) {
        assembly { i := mload(add(b, 0x14)) }
        uint160 a = uint160(i);
        add = address(i);
    }

    function bytesToBytes32(bytes b, bytes32 i) private returns (bytes32 bts) {
        assembly { i := mload(add(b, 0x20)) }
        bts = i;
    }

    // empty function to handle wrong calls
    function () { throw; }
}


add token burning in refund

add token burning in refund

pragma solidity ^0.4.8;
import "./SafeMath.sol";
import "./RLC.sol";
import "./PullPayment.sol";
import "./Pausable.sol";

/*
  Crowdsale Smart Contract for the iEx.ec project

  This smart contract collects ETH and BTC, and in return emits RLC tokens to the backers

  Thanks to BeyondTheVoid and TokenMarket who helped us shaping this code.

*/

contract Crowdsale is SafeMath, PullPayment, Pausable {

  	struct Backer {
  	  uint weiReceived;	// Amount of ETH given
	  string btc_address;  //store the btc address for full tracability
	  uint satoshiReceived;	// Amount of BTC given
	  uint rlcToSend;   	// rlc to distribute when the min cap is reached
	  uint rlcSent;
	}

	RLC 	public rlc;         // RLC contract reference
	address public owner;       // Contract owner (iEx.ec team)
	address public multisigETH; // Multisig contract that will receive the ETH
	address public BTCproxy;	// addess of the BTC Proxy

	uint public RLCPerETH;      // Number of RLC per ETH
	uint public RLCPerSATOSHI;  // Number of RLC per SATOSHI
	uint public ETHReceived;    // Number of ETH received
	uint public BTCReceived;    // Number of BTC received
	uint public RLCSentToETH;   // Number of RLC sent to ETH contributors
	uint public RLCSentToBTC;   // Number of RLC sent to BTC contributors
	uint public startBlock;     // Crowdsale start block
	uint public endBlock;       // Crowdsale end block
	uint public minCap;         // Minimum number of RLC to sell
	uint public maxCap;         // Maximum number of RLC to sell
	bool public maxCapReached;  // Max cap has been reached
	uint public minInvestETH;   // Minimum amount to invest
	uint public minInvestBTC;   // Minimum amount to invest
	bool public crowdsaleClosed;// Is crowdsale still on going

	address public bounty;		// address at which the bounty RLC will be sent
	address public reserve; 	// address at which the contingency reserve will be sent
	address public team;		// address at which the team RLC will be sent

	uint public rlc_bounty;		// amount of bounties RLC
	uint public rlc_reserve;	// amount of the contingency reserve
	uint public rlc_team;		// amount of the team RLC
	mapping(address => Backer) public backers; //backersETH indexed by their ETH address

    // Auth modifier, if the msg.sender isn't the expected address, throw.
	modifier onlyBy(address a){
	    if (msg.sender != a) throw;
	    _;
	}

	modifier minCapNotReached() {
		if ((now<endBlock) || RLCSentToETH + RLCSentToBTC > minCap ) throw;
		_;
	}

	modifier respectTimeFrame() {
		if ((now < startBlock) || (now > endBlock )) throw;
		_;
	}

	/*
	 * Event
	 */
	event ReceivedETH(address addr, uint value);
	event ReceivedBTC(address addr, string from, uint value, string txid);
	event RefundBTC(string to, uint value);
	event Logs(address indexed from, uint amount, string value);

	/*
	 *	Constructor
	 */
	function Crowdsale(address _token, address _btcproxy) {

		//set the different variables
		owner = msg.sender;
		BTCproxy = _btcproxy; // to change
		rlc = RLC(_token); 	// RLC contract address
		multisigETH = 0x8cd6B3D8713df6aA35894c8beA200c27Ebe92550;
		team = 0x1000000000000000000000000000000000000000;
		reserve = 0x2000000000000000000000000000000000000000;
		bounty = 0x3000000000000000000000000000000000000000;
		RLCSentToETH = 0;
		RLCSentToBTC = 0;
		minInvestETH = 100 finney; // 0.1 ether
		minInvestBTC = 1000000;     // approx 10 USD or 0.01000000 BTC
		startBlock = 0 ;            	// should wait for the call of the function start
		endBlock =  0;  				// should wait for the call of the function start
		RLCPerETH = 200000000000;    // FIXME  will be update
		RLCPerSATOSHI = 50000;         // = 5000,000000000 RLC per BTC
		minCap=12000000000000000;
		maxCap=60000000000000000;
		rlc_bounty=1700000000000000;		// max 6000000 RLC
		rlc_reserve=1700000000000000;		// max 6000000 RLC
		rlc_team=12000000000000000;
	}

	/*
	 * The fallback function corresponds to a donation in ETH
	 */
	function() payable {
		if (now > endBlock) throw;
		receiveETH(msg.sender);
	}

	/*
	 * To call to start the crowdsale
	 */
	function start() onlyBy(owner) {
		startBlock = now ;
		endBlock =  now + 30 days;
	}

	/*
	 *	Receives a donation in ETH
	 */
	function receiveETH(address beneficiary) internal stopInEmergency respectTimeFrame {

		//don't accept funding under a predefined treshold
		if (msg.value < minInvestETH) throw;

		//compute the number of RLC to send
		uint rlcToSend = bonus(safeMul(msg.value,RLCPerETH)/(1 ether));

		// check if we are not reaching the maxCap by accepting this donation
		if (safeAdd(rlcToSend, safeAdd(RLCSentToETH, RLCSentToBTC)) > maxCap) throw;

		//update the backer
		Backer backer = backers[beneficiary];

		if (!rlc.transfer(beneficiary, rlcToSend)) throw;     // Do the transfer right now

		backer.rlcSent = safeAdd(backer.rlcSent, rlcToSend);
		backer.weiReceived = safeAdd(backer.weiReceived, msg.value); // Update the total wei collcted during the crowdfunding
		ETHReceived = safeAdd(ETHReceived, msg.value); // Update the total wei collcted during the crowdfunding
		RLCSentToETH = safeAdd(RLCSentToETH, rlcToSend);

		emitRLC(rlcToSend);
		// send the corresponding contribution event
		ReceivedETH(beneficiary,ETHReceived);
	}

	/*
	 * receives a donation in BTC
	 */
	function receiveBTC(address beneficiary, string btc_address, uint value, string txid) stopInEmergency respectTimeFrame onlyBy(BTCproxy) returns (bool res){

		if (value < minInvestBTC) throw;  // the verif is made by btcproxy

		//compute the number of RLC to send
		uint rlcToSend = bonus(safeMul(value,RLCPerSATOSHI));
		if (safeAdd(rlcToSend, safeAdd(RLCSentToETH, RLCSentToBTC)) > maxCap) {
			RefundBTC(btc_address , value);
			return false;
		}

		//update the backer
		Backer backer = backers[beneficiary];

		if (!rlc.transfer(beneficiary, rlcToSend)) throw;     // Do the transfer right now

		backer.rlcSent = safeAdd(backer.rlcSent , rlcToSend);
		backer.btc_address = btc_address;
		backer.satoshiReceived = safeAdd(backer.satoshiReceived, value);
		BTCReceived =  safeAdd(BTCReceived, value);// Update the total satoshi collected during the crowdfunding
		RLCSentToBTC = safeAdd(RLCSentToBTC, rlcToSend);
		emitRLC(rlcToSend);
		ReceivedBTC(beneficiary, btc_address, rlcToSend, txid);
		return true;
	}

	/*
	 * Compute the varaible part
	 */
	function emitRLC(uint amount) internal {
		rlc_bounty = safeAdd(rlc_bounty, amount/10);
		rlc_team = safeAdd(rlc_team, amount/20);
		rlc_reserve = safeAdd(rlc_reserve, amount/10);
		Logs(msg.sender ,amount, "emitRLC");
	}

	/*
	 * Compute the RLC bonus according to the investment period
	 */
	function bonus(uint amount) internal constant returns (uint) {
		if (now < safeAdd(startBlock, 10 days)) return (safeAdd(amount, amount/5));   // bonus 20%
		if (now < safeAdd(startBlock, 20 days)) return (safeAdd(amount, amount/10));  // bonus 10%
		return amount;
	}

	/*
	 * When mincap is not reach backer can call the approveAndCall function of the RLC token contract
	 * whith this crowdsale contract on parameter with all the RLC they get in order to be refund
	 */
	function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData, bytes _extraData2) minCapNotReached public {
		if (msg.sender != address(rlc)) throw;
		if (_extraData.length != 0) throw;  // no extradata needed
		if (_extraData2.length != 0) throw;  // no extradata needed
		if (_value != backers[_from].rlcSent) throw; // compare value from backer balance
		if (!rlc.transferFrom(_from, address(this), _value)) throw ; // get the token back to the crowdsale contract
		if (!rlc.burn(_value)) throw ;
		uint ETHToSend = backers[_from].weiReceived;
		backers[_from].weiReceived=0;
		uint BTCToSend = backers[_from].satoshiReceived;
		backers[_from].satoshiReceived = 0;
		if (ETHToSend > 0) {
			asyncSend(_from,ETHToSend);
		}
		if (BTCToSend > 0)
			RefundBTC(backers[_from].btc_address ,BTCToSend); // event message to manually refund BTC
	}

	/*
	* Update the rate RLC per ETH, computed externally by using the BTCETH index on kraken every 10min
	*/
	function setRLCPerETH(uint rate) onlyBy(BTCproxy) {
		RLCPerETH=rate;
	}

	/*
	* Finalize the crowdsale, should be called after the refund period
	*/
	function finalize() onlyBy(owner) {
		// check
		if (RLCSentToETH + RLCSentToBTC < maxCap - 100000000000 && now < endBlock) throw; // cannot finalise before 30 day until maxcap is reached
		if (RLCSentToETH + RLCSentToBTC < minCap && now < endBlock + 15 days) throw ;  // if mincap is not reached donator have 15days to get refund
		//moves the remaining ETH to the multisig address
		if (!multisigETH.send(this.balance)) throw;
		//moves RLC to the team, reserve and bounty address
		if (rlc_reserve > 6000000000000000){
			if(!rlc.transfer(reserve,6000000000000000)) throw; // max cap 6000000RLC
			rlc_reserve = 6000000000000000;
		} else {
			if(!rlc.transfer(reserve,rlc_reserve)) throw;
		}
		if (rlc_bounty > 6000000000000000){
			if(!rlc.transfer(bounty,6000000000000000)) throw; // max cap 6000000RLC
			rlc_bounty = 6000000000000000;
		} else {
			if(!rlc.transfer(bounty,rlc_bounty)) throw;
		}
		if (!rlc.transfer(team,rlc_team)) throw;
		uint RLCEmitted = rlc_reserve + rlc_bounty + rlc_team + RLCSentToBTC + RLCSentToETH;
		if (RLCEmitted < rlc.totalSupply())					// necessary check for reserve and bounty maxcap
			  rlc.burn(rlc.totalSupply() - RLCEmitted);
		rlc.unlock();
			crowdsaleClosed = true;
	}
}

pragma solidity ^0.4.8;
import "./SafeMath.sol";
import "./RLC.sol";
import "./PullPayment.sol";
import "./Pausable.sol";

/*
  Crowdsale Smart Contract for the iEx.ec project

  This smart contract collects ETH and BTC, and in return emits RLC tokens to the backers

  Thanks to BeyondTheVoid and TokenMarket who helped us shaping this code.

*/

contract Crowdsale is SafeMath, PullPayment, Pausable {

  	struct Backer {
  	  uint weiReceived;	// Amount of ETH given
	  string btc_address;  //store the btc address for full tracability
	  uint satoshiReceived;	// Amount of BTC given
	  uint rlcToSend;   	// rlc to distribute when the min cap is reached
	  uint rlcSent;
	}

	RLC 	public rlc;         // RLC contract reference
	address public owner;       // Contract owner (iEx.ec team)
	address public multisigETH; // Multisig contract that will receive the ETH
	address public BTCproxy;	// addess of the BTC Proxy

	uint public RLCPerETH;      // Number of RLC per ETH
	uint public RLCPerSATOSHI;  // Number of RLC per SATOSHI
	uint public ETHReceived;    // Number of ETH received
	uint public BTCReceived;    // Number of BTC received
	uint public RLCSentToETH;   // Number of RLC sent to ETH contributors
	uint public RLCSentToBTC;   // Number of RLC sent to BTC contributors
	uint public startBlock;     // Crowdsale start block
	uint public endBlock;       // Crowdsale end block
	uint public minCap;         // Minimum number of RLC to sell
	uint public maxCap;         // Maximum number of RLC to sell
	bool public maxCapReached;  // Max cap has been reached
	uint public minInvestETH;   // Minimum amount to invest
	uint public minInvestBTC;   // Minimum amount to invest
	bool public crowdsaleClosed;// Is crowdsale still on going

	address public bounty;		// address at which the bounty RLC will be sent
	address public reserve; 	// address at which the contingency reserve will be sent
	address public team;		// address at which the team RLC will be sent

	uint public rlc_bounty;		// amount of bounties RLC
	uint public rlc_reserve;	// amount of the contingency reserve
	uint public rlc_team;		// amount of the team RLC
	mapping(address => Backer) public backers; //backersETH indexed by their ETH address

    // Auth modifier, if the msg.sender isn't the expected address, throw.
	modifier onlyBy(address a){
	    if (msg.sender != a) throw;
	    _;
	}

	modifier minCapNotReached() {
		if ((now<endBlock) || RLCSentToETH + RLCSentToBTC > minCap ) throw;
		_;
	}

	modifier respectTimeFrame() {
		if ((now < startBlock) || (now > endBlock )) throw;
		_;
	}

	/*
	 * Event
	 */
	event ReceivedETH(address addr, uint value);
	event ReceivedBTC(address addr, string from, uint value, string txid);
	event RefundBTC(string to, uint value);
	event Logs(address indexed from, uint amount, string value);

	/*
	 *	Constructor
	 */
	function Crowdsale(address _token, address _btcproxy) {

		//set the different variables
		owner = msg.sender;
		BTCproxy = _btcproxy; // to change
		rlc = RLC(_token); 	// RLC contract address
		multisigETH = 0x8cd6B3D8713df6aA35894c8beA200c27Ebe92550;
		team = 0x1000000000000000000000000000000000000000;
		reserve = 0x2000000000000000000000000000000000000000;
		bounty = 0x3000000000000000000000000000000000000000;
		RLCSentToETH = 0;
		RLCSentToBTC = 0;
		minInvestETH = 100 finney; // 0.1 ether
		minInvestBTC = 1000000;     // approx 10 USD or 0.01000000 BTC
		startBlock = 0 ;            	// should wait for the call of the function start
		endBlock =  0;  				// should wait for the call of the function start
		RLCPerETH = 200000000000;    // FIXME  will be update
		RLCPerSATOSHI = 50000;         // = 5000,000000000 RLC per BTC
		minCap=12000000000000000;
		maxCap=60000000000000000;
		rlc_bounty=1700000000000000;		// max 6000000 RLC
		rlc_reserve=1700000000000000;		// max 6000000 RLC
		rlc_team=12000000000000000;
	}

	/*
	 * The fallback function corresponds to a donation in ETH
	 */
	function() payable {
		if (now > endBlock) throw;
		receiveETH(msg.sender);
	}

	/*
	 * To call to start the crowdsale
	 */
	function start() onlyBy(owner) {
		startBlock = now ;
		endBlock =  now + 30 days;
	}

	/*
	 *	Receives a donation in ETH
	 */
	function receiveETH(address beneficiary) internal stopInEmergency respectTimeFrame {

		//don't accept funding under a predefined treshold
		if (msg.value < minInvestETH) throw;

		//compute the number of RLC to send
		uint rlcToSend = bonus(safeMul(msg.value,RLCPerETH)/(1 ether));

		// check if we are not reaching the maxCap by accepting this donation
		if (safeAdd(rlcToSend, safeAdd(RLCSentToETH, RLCSentToBTC)) > maxCap) throw;

		//update the backer
		Backer backer = backers[beneficiary];

		if (!rlc.transfer(beneficiary, rlcToSend)) throw;     // Do the transfer right now

		backer.rlcSent = safeAdd(backer.rlcSent, rlcToSend);
		backer.weiReceived = safeAdd(backer.weiReceived, msg.value); // Update the total wei collcted during the crowdfunding
		ETHReceived = safeAdd(ETHReceived, msg.value); // Update the total wei collcted during the crowdfunding
		RLCSentToETH = safeAdd(RLCSentToETH, rlcToSend);

		emitRLC(rlcToSend);
		// send the corresponding contribution event
		ReceivedETH(beneficiary,ETHReceived);
	}

	/*
	 * receives a donation in BTC
	 */
	function receiveBTC(address beneficiary, string btc_address, uint value, string txid) stopInEmergency respectTimeFrame onlyBy(BTCproxy) returns (bool res){

		if (value < minInvestBTC) throw;  // the verif is made by btcproxy

		//compute the number of RLC to send
		uint rlcToSend = bonus(safeMul(value,RLCPerSATOSHI));
		if (safeAdd(rlcToSend, safeAdd(RLCSentToETH, RLCSentToBTC)) > maxCap) {
			RefundBTC(btc_address , value);
			return false;
		}

		//update the backer
		Backer backer = backers[beneficiary];

		if (!rlc.transfer(beneficiary, rlcToSend)) throw;     // Do the transfer right now

		backer.rlcSent = safeAdd(backer.rlcSent , rlcToSend);
		backer.btc_address = btc_address;
		backer.satoshiReceived = safeAdd(backer.satoshiReceived, value);
		BTCReceived =  safeAdd(BTCReceived, value);// Update the total satoshi collected during the crowdfunding
		RLCSentToBTC = safeAdd(RLCSentToBTC, rlcToSend);
		emitRLC(rlcToSend);
		ReceivedBTC(beneficiary, btc_address, rlcToSend, txid);
		return true;
	}

	/*
	 * Compute the varaible part
	 */
	function emitRLC(uint amount) internal {
		rlc_bounty = safeAdd(rlc_bounty, amount/10);
		rlc_team = safeAdd(rlc_team, amount/20);
		rlc_reserve = safeAdd(rlc_reserve, amount/10);
		Logs(msg.sender ,amount, "emitRLC");
	}

	/*
	 * Compute the RLC bonus according to the investment period
	 */
	function bonus(uint amount) internal constant returns (uint) {
		if (now < safeAdd(startBlock, 10 days)) return (safeAdd(amount, amount/5));   // bonus 20%
		if (now < safeAdd(startBlock, 20 days)) return (safeAdd(amount, amount/10));  // bonus 10%
		return amount;
	}

	/*
	 * When mincap is not reach backer can call the approveAndCall function of the RLC token contract
	 * whith this crowdsale contract on parameter with all the RLC they get in order to be refund
	 */
	function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData, bytes _extraData2) minCapNotReached public {
		if (msg.sender != address(rlc)) throw;
		if (_extraData.length != 0) throw;  // no extradata needed
		if (_extraData2.length != 0) throw;  // no extradata needed
		if (_value != backers[_from].rlcSent) throw; // compare value from backer balance
		if (!rlc.transferFrom(_from, address(this), _value)) throw ; // get the token back to the crowdsale contract
		uint ETHToSend = backers[_from].weiReceived;
		backers[_from].weiReceived=0;
		uint BTCToSend = backers[_from].satoshiReceived;
		backers[_from].satoshiReceived = 0;
		if (ETHToSend > 0) {
			asyncSend(_from,ETHToSend);
		}
		if (BTCToSend > 0)
			RefundBTC(backers[_from].btc_address ,BTCToSend); // event message to manually refund BTC
	}

	/*
	* Update the rate RLC per ETH, computed externally by using the BTCETH index on kraken every 10min
	*/
	function setRLCPerETH(uint rate) onlyBy(BTCproxy) {
		RLCPerETH=rate;
	}

	/*
	* Finalize the crowdsale, should be called after the refund period
	*/
	function finalize() onlyBy(owner) {
		// check
		if (RLCSentToETH + RLCSentToBTC < maxCap - 100000000000 && now < endBlock) throw; // cannot finalise before 30 day until maxcap is reached
		if (RLCSentToETH + RLCSentToBTC < minCap && now < endBlock + 15 days) throw ;  // if mincap is not reached donator have 15days to get refund
		//moves the remaining ETH to the multisig address
		if (!multisigETH.send(this.balance)) throw;
		//moves RLC to the team, reserve and bounty address
		if (rlc_reserve > 6000000000000000){
			if(!rlc.transfer(reserve,6000000000000000)) throw; // max cap 6000000RLC
			rlc_reserve = 6000000000000000;
		} else {
			if(!rlc.transfer(reserve,rlc_reserve)) throw;
		}
		if (rlc_bounty > 6000000000000000){
			if(!rlc.transfer(bounty,6000000000000000)) throw; // max cap 6000000RLC
			rlc_bounty = 6000000000000000;
		} else {
			if(!rlc.transfer(bounty,rlc_bounty)) throw;
		}
		if (!rlc.transfer(team,rlc_team)) throw;
		uint RLCEmitted = rlc_reserve + rlc_bounty + rlc_team + RLCSentToBTC + RLCSentToETH;
		if (RLCEmitted < rlc.totalSupply())					// necessary check for reserve and bounty maxcap
			  rlc.burn(rlc.totalSupply() - RLCEmitted);
		rlc.unlock();
			crowdsaleClosed = true;
	}
}


Removed unnecessary Secondary inheritance from RefundEscrow. (#1381)

pragma solidity ^0.4.24;

import "./ConditionalEscrow.sol";

/**
 * @title RefundEscrow
 * @dev Escrow that holds funds for a beneficiary, deposited from multiple parties.
 * The primary account may close the deposit period, and allow for either withdrawal
 * by the beneficiary, or refunds to the depositors.
 */
contract RefundEscrow is ConditionalEscrow {
  enum State { Active, Refunding, Closed }

  event Closed();
  event RefundsEnabled();

  State private _state;
  address private _beneficiary;

  /**
   * @dev Constructor.
   * @param beneficiary The beneficiary of the deposits.
   */
  constructor(address beneficiary) public {
    require(beneficiary != address(0));
    _beneficiary = beneficiary;
    _state = State.Active;
  }

  /**
   * @return the current state of the escrow.
   */
  function state() public view returns (State) {
    return _state;
  }

  /**
   * @return the beneficiary of the escrow.
   */
  function beneficiary() public view returns (address) {
    return _beneficiary;
  }

  /**
   * @dev Stores funds that may later be refunded.
   * @param refundee The address funds will be sent to if a refund occurs.
   */
  function deposit(address refundee) public payable {
    require(_state == State.Active);
    super.deposit(refundee);
  }

  /**
   * @dev Allows for the beneficiary to withdraw their funds, rejecting
   * further deposits.
   */
  function close() public onlyPrimary {
    require(_state == State.Active);
    _state = State.Closed;
    emit Closed();
  }

  /**
   * @dev Allows for refunds to take place, rejecting further deposits.
   */
  function enableRefunds() public onlyPrimary {
    require(_state == State.Active);
    _state = State.Refunding;
    emit RefundsEnabled();
  }

  /**
   * @dev Withdraws the beneficiary's funds.
   */
  function beneficiaryWithdraw() public {
    require(_state == State.Closed);
    _beneficiary.transfer(address(this).balance);
  }

  /**
   * @dev Returns whether refundees can withdraw their deposits (be refunded).
   */
  function withdrawalAllowed(address payee) public view returns (bool) {
    return _state == State.Refunding;
  }
}

pragma solidity ^0.4.24;

import "./ConditionalEscrow.sol";
import "../ownership/Secondary.sol";

/**
 * @title RefundEscrow
 * @dev Escrow that holds funds for a beneficiary, deposited from multiple parties.
 * The primary account may close the deposit period, and allow for either withdrawal
 * by the beneficiary, or refunds to the depositors.
 */
contract RefundEscrow is Secondary, ConditionalEscrow {
  enum State { Active, Refunding, Closed }

  event Closed();
  event RefundsEnabled();

  State private _state;
  address private _beneficiary;

  /**
   * @dev Constructor.
   * @param beneficiary The beneficiary of the deposits.
   */
  constructor(address beneficiary) public {
    require(beneficiary != address(0));
    _beneficiary = beneficiary;
    _state = State.Active;
  }

  /**
   * @return the current state of the escrow.
   */
  function state() public view returns (State) {
    return _state;
  }

  /**
   * @return the beneficiary of the escrow.
   */
  function beneficiary() public view returns (address) {
    return _beneficiary;
  }

  /**
   * @dev Stores funds that may later be refunded.
   * @param refundee The address funds will be sent to if a refund occurs.
   */
  function deposit(address refundee) public payable {
    require(_state == State.Active);
    super.deposit(refundee);
  }

  /**
   * @dev Allows for the beneficiary to withdraw their funds, rejecting
   * further deposits.
   */
  function close() public onlyPrimary {
    require(_state == State.Active);
    _state = State.Closed;
    emit Closed();
  }

  /**
   * @dev Allows for refunds to take place, rejecting further deposits.
   */
  function enableRefunds() public onlyPrimary {
    require(_state == State.Active);
    _state = State.Refunding;
    emit RefundsEnabled();
  }

  /**
   * @dev Withdraws the beneficiary's funds.
   */
  function beneficiaryWithdraw() public {
    require(_state == State.Closed);
    _beneficiary.transfer(address(this).balance);
  }

  /**
   * @dev Returns whether refundees can withdraw their deposits (be refunded).
   */
  function withdrawalAllowed(address payee) public view returns (bool) {
    return _state == State.Refunding;
  }
}


Update IERC777.sol (#2184)

Update IERC777.sol (#2184)

pragma solidity ^0.6.0;

/**
 * @dev Interface of the ERC777Token standard as defined in the EIP.
 *
 * This contract uses the
 * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
 * token holders and recipients react to token movements by using setting implementers
 * for the associated interfaces in said registry. See {IERC1820Registry} and
 * {ERC1820Implementer}.
 */
interface IERC777 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the smallest part of the token that is not divisible. This
     * means all token operations (creation, movement and destruction) must have
     * amounts that are a multiple of this number.
     *
     * For most token contracts, this value will equal 1.
     */
    function granularity() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by an account (`owner`).
     */
    function balanceOf(address owner) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * If send or receive hooks are registered for the caller and `recipient`,
     * the corresponding functions will be called with `data` and empty
     * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
     *
     * Emits a {Sent} event.
     *
     * Requirements
     *
     * - the caller must have at least `amount` tokens.
     * - `recipient` cannot be the zero address.
     * - if `recipient` is a contract, it must implement the {IERC777Recipient}
     * interface.
     */
    function send(address recipient, uint256 amount, bytes calldata data) external;

    /**
     * @dev Destroys `amount` tokens from the caller's account, reducing the
     * total supply.
     *
     * If a send hook is registered for the caller, the corresponding function
     * will be called with `data` and empty `operatorData`. See {IERC777Sender}.
     *
     * Emits a {Burned} event.
     *
     * Requirements
     *
     * - the caller must have at least `amount` tokens.
     */
    function burn(uint256 amount, bytes calldata data) external;

    /**
     * @dev Returns true if an account is an operator of `tokenHolder`.
     * Operators can send and burn tokens on behalf of their owners. All
     * accounts are their own operator.
     *
     * See {operatorSend} and {operatorBurn}.
     */
    function isOperatorFor(address operator, address tokenHolder) external view returns (bool);

    /**
     * @dev Make an account an operator of the caller.
     *
     * See {isOperatorFor}.
     *
     * Emits an {AuthorizedOperator} event.
     *
     * Requirements
     *
     * - `operator` cannot be calling address.
     */
    function authorizeOperator(address operator) external;

    /**
     * @dev Revoke an account's operator status for the caller.
     *
     * See {isOperatorFor} and {defaultOperators}.
     *
     * Emits a {RevokedOperator} event.
     *
     * Requirements
     *
     * - `operator` cannot be calling address.
     */
    function revokeOperator(address operator) external;

    /**
     * @dev Returns the list of default operators. These accounts are operators
     * for all token holders, even if {authorizeOperator} was never called on
     * them.
     *
     * This list is immutable, but individual holders may revoke these via
     * {revokeOperator}, in which case {isOperatorFor} will return false.
     */
    function defaultOperators() external view returns (address[] memory);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
     * be an operator of `sender`.
     *
     * If send or receive hooks are registered for `sender` and `recipient`,
     * the corresponding functions will be called with `data` and
     * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
     *
     * Emits a {Sent} event.
     *
     * Requirements
     *
     * - `sender` cannot be the zero address.
     * - `sender` must have at least `amount` tokens.
     * - the caller must be an operator for `sender`.
     * - `recipient` cannot be the zero address.
     * - if `recipient` is a contract, it must implement the {IERC777Recipient}
     * interface.
     */
    function operatorSend(
        address sender,
        address recipient,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the total supply.
     * The caller must be an operator of `account`.
     *
     * If a send hook is registered for `account`, the corresponding function
     * will be called with `data` and `operatorData`. See {IERC777Sender}.
     *
     * Emits a {Burned} event.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     * - the caller must be an operator for `account`.
     */
    function operatorBurn(
        address account,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;

    event Sent(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 amount,
        bytes data,
        bytes operatorData
    );

    event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);

    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);

    event AuthorizedOperator(address indexed operator, address indexed tokenHolder);

    event RevokedOperator(address indexed operator, address indexed tokenHolder);
}

pragma solidity ^0.6.0;

/**
 * @dev Interface of the ERC777Token standard as defined in the EIP.
 *
 * This contract uses the
 * https://eips.ethereum.org/EIPS/eip-1820[ERC1820 registry standard] to let
 * token holders and recipients react to token movements by using setting implementers
 * for the associated interfaces in said registry. See {IERC1820Registry} and
 * {ERC1820Implementer}.
 */
interface IERC777 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the smallest part of the token that is not divisible. This
     * means all token operations (creation, movement and destruction) must have
     * amounts that are a multiple of this number.
     *
     * For most token contracts, this value will equal 1.
     */
    function granularity() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by an account (`owner`).
     */
    function balanceOf(address owner) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `recipient`.
     *
     * If send or receive hooks are registered for the caller and `recipient`,
     * the corresponding functions will be called with `data` and empty
     * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
     *
     * Emits a {Sent} event.
     *
     * Requirements
     *
     * - the caller must have at least `amount` tokens.
     * - `recipient` cannot be the zero address.
     * - if `recipient` is a contract, it must implement the {IERC777Recipient}
     * interface.
     */
    function send(address recipient, uint256 amount, bytes calldata data) external;

    /**
     * @dev Destroys `amount` tokens from the caller's account, reducing the
     * total supply.
     *
     * If a send hook is registered for the caller, the corresponding function
     * will be called with `data` and empty `operatorData`. See {IERC777Sender}.
     *
     * Emits a {Burned} event.
     *
     * Requirements
     *
     * - the caller must have at least `amount` tokens.
     */
    function burn(uint256 amount, bytes calldata data) external;

    /**
     * @dev Returns true if an account is an operator of `tokenHolder`.
     * Operators can send and burn tokens on behalf of their owners. All
     * accounts are their own operator.
     *
     * See {operatorSend} and {operatorBurn}.
     */
    function isOperatorFor(address operator, address tokenHolder) external view returns (bool);

    /**
     * @dev Make an account an operator of the caller.
     *
     * See {isOperatorFor}.
     *
     * Emits an {AuthorizedOperator} event.
     *
     * Requirements
     *
     * - `operator` cannot be calling address.
     */
    function authorizeOperator(address operator) external;

    /**
     * @dev Make an account an operator of the caller.
     *
     * See {isOperatorFor} and {defaultOperators}.
     *
     * Emits a {RevokedOperator} event.
     *
     * Requirements
     *
     * - `operator` cannot be calling address.
     */
    function revokeOperator(address operator) external;

    /**
     * @dev Returns the list of default operators. These accounts are operators
     * for all token holders, even if {authorizeOperator} was never called on
     * them.
     *
     * This list is immutable, but individual holders may revoke these via
     * {revokeOperator}, in which case {isOperatorFor} will return false.
     */
    function defaultOperators() external view returns (address[] memory);

    /**
     * @dev Moves `amount` tokens from `sender` to `recipient`. The caller must
     * be an operator of `sender`.
     *
     * If send or receive hooks are registered for `sender` and `recipient`,
     * the corresponding functions will be called with `data` and
     * `operatorData`. See {IERC777Sender} and {IERC777Recipient}.
     *
     * Emits a {Sent} event.
     *
     * Requirements
     *
     * - `sender` cannot be the zero address.
     * - `sender` must have at least `amount` tokens.
     * - the caller must be an operator for `sender`.
     * - `recipient` cannot be the zero address.
     * - if `recipient` is a contract, it must implement the {IERC777Recipient}
     * interface.
     */
    function operatorSend(
        address sender,
        address recipient,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the total supply.
     * The caller must be an operator of `account`.
     *
     * If a send hook is registered for `account`, the corresponding function
     * will be called with `data` and `operatorData`. See {IERC777Sender}.
     *
     * Emits a {Burned} event.
     *
     * Requirements
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     * - the caller must be an operator for `account`.
     */
    function operatorBurn(
        address account,
        uint256 amount,
        bytes calldata data,
        bytes calldata operatorData
    ) external;

    event Sent(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256 amount,
        bytes data,
        bytes operatorData
    );

    event Minted(address indexed operator, address indexed to, uint256 amount, bytes data, bytes operatorData);

    event Burned(address indexed operator, address indexed from, uint256 amount, bytes data, bytes operatorData);

    event AuthorizedOperator(address indexed operator, address indexed tokenHolder);

    event RevokedOperator(address indexed operator, address indexed tokenHolder);
}


Corrected interface.

Corrected interface.

pragma solidity ^0.4.15;

/// @title ERC Token Standard #20 Interface (https://github.com/ethereum/EIPs/issues/20)
contract ERC20 {
    uint256 public totalSupply;
    function balanceOf(address _owner) constant returns (uint256 balance);
    function transfer(address _to, uint256 _value) returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success);
    function approve(address _spender, uint256 _value) returns (bool success);
    function allowance(address _owner, address _spender) constant returns (uint256 remaining);
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
}

pragma solidity ^0.4.15;

/// @title ERC Token Standard #20 Interface (https://github.com/ethereum/EIPs/issues/20)
contract ERC20 {
    uint public totalSupply;
    function balanceOf(address _owner) constant returns (uint balance);
    function transfer(address _to, uint _value) returns (bool success);
    function transferFrom(address _from, address _to, uint _value) returns (bool success);
    function approve(address _spender, uint _value) returns (bool success);
    function allowance(address _owner, address _spender) constant returns (uint remaining);
    event Transfer(address indexed _from, address indexed _to, uint _value);
    event Approval(address indexed _owner, address indexed _spender, uint _value);
}