updated BancorChanger comments

updated BancorChanger comments

pragma solidity ^0.4.10;
import './BancorEventsDispatcher.sol';
import './TokenChangerInterface.sol';
import './SmartTokenInterface.sol';

/*
    Open issues:
    - add miner abuse protection
    - assumes that the reserve tokens either return true for transfer/transferFrom or throw - possibly remove the reliance on the return value
*/

// interfaces

contract BancorFormula {
    function calculatePurchaseReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _depositAmount) public constant returns (uint256 amount);
    function calculateSaleReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _sellAmount) public constant returns (uint256 amount);
}

/*
    Bancor Changer v0.1
*/
contract BancorChanger is BancorEventsDispatcher, TokenChangerInterface {
    struct Reserve {
        uint8 ratio;    // constant reserve ratio (CRR), 1-100
        bool isEnabled; // is purchase of the smart token enabled with the reserve, can be set by the owner
        bool isSet;     // is the reserve set, used to tell if the mapping element is defined
    }

    SmartTokenInterface public token;               // smart token governed by the changer
    BancorFormula public formula;                   // bancor calculation formula contract
    bool public isActive = false;                   // true if the change functionality can now be used, false if not
    address[] public reserveTokens;                 // ERC20 standard token addresses
    mapping (address => Reserve) public reserves;   // reserve token addresses -> reserve data
    uint8 private totalReserveRatio = 0;            // used to prevent increasing the total reserve ratio above 100% efficiently

    // events, can be used to listen to the contract directly, as opposed to through the events contract
    event Change(address indexed _fromToken, address indexed _toToken, address indexed _trader, uint256 _amount, uint256 _return);

    /*
        _token      smart token governed by the changer
        _formula    address of a bancor formula contract
        _events     optional, address of a bancor events contract
    */
    function BancorChanger(address _token, address _formula, address _events)
        BancorEventsDispatcher(_events)
        validAddress(_token)
        validAddress(_formula)
    {
        token = SmartTokenInterface(_token);
        formula = BancorFormula(_formula);
    }

    // validates an address - currently only checks that it isn't null
    modifier validAddress(address _address) {
        assert(_address != 0x0);
        _;
    }

    // validates a reserve token address - verifies that the address belongs to one of the reserve tokens
    modifier validReserve(address _address) {
        assert(reserves[_address].isSet);
        _;
    }

    // validates a token address - verifies that the address belongs to one of the changable tokens
    modifier validToken(address _address) {
        assert(_address == address(token) || reserves[_address].isSet);
        _;
    }

    // ensures that token changing is active
    modifier active() {
        assert(isActive);
        _;
    }

    // ensures that token changing is not active
    modifier inactive() {
        assert(!isActive);
        _;
    }

    /*
        returns the number of reserve tokens defined
    */
    function reserveTokenCount() public constant returns (uint16 count) {
        return uint16(reserveTokens.length);
    }

    /*
        returns the number of changeable tokens supported by the contract
        note that the number of changable tokens is the number of reserve token, plus 1 (that represents the smart token)
    */
    function changeableTokenCount() public constant returns (uint16 count) {
        return reserveTokenCount() + 1;
    }

    /*
        given a changable token index, returns the changable token contract address
    */
    function changeableToken(uint16 _tokenIndex) public constant returns (address tokenAddress) {
        if (_tokenIndex == 0)
            return token;
        return reserveTokens[_tokenIndex - 1];
    }

    /*
        defines a new reserve for the token
        can only be called by the changer owner while the changer is inactive

        _token  address of the reserve token
        _ratio  constant reserve ratio, 1-100
    */
    function addReserve(address _token, uint8 _ratio)
        public
        ownerOnly
        inactive
        validAddress(_token)
        returns (bool success)
    {
        require(_token != address(this) && _token != address(token) && !reserves[_token].isSet && _ratio > 0 && _ratio <= 100 && totalReserveRatio + _ratio <= 100); // validate input

        reserves[_token].ratio = _ratio;
        reserves[_token].isEnabled = true;
        reserves[_token].isSet = true;
        reserveTokens.push(_token);
        totalReserveRatio += _ratio;
        return true;
    }

    /*
        allows the owner to execute the smart token's issue function

        _to         account to receive the new amount
        _amount     amount to increase the supply by
    */
    function issueToken(address _to, uint256 _amount) public ownerOnly returns (bool success) {
        return token.issue(_to, _amount);
    }

    /*
        allows the owner to execute the smart token's destroy function

        _from       account to remove the new amount from
        _amount     amount to decrease the supply by
    */
    function destroyToken(address _from, uint256 _amount) public ownerOnly returns (bool success) {
        return token.destroy(_from, _amount);
    }

    /*
        withdraws tokens from the reserve and sends them to an account
        can only be called by the changer owner while the changer is inactive

        _reserveToken    reserve token contract address
        _to              account to receive the new amount
        _amount          amount to withdraw (in the reserve token)
    */
    function withdraw(address _reserveToken, address _to, uint256 _amount)
        public
        ownerOnly
        validReserve(_reserveToken)
        validAddress(_to)
        returns (bool success)
    {
        require(_to != address(this) && _amount != 0); // validate input
        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        return reserveToken.transfer(_to, _amount);
    }

    /*
        disables purchasing with the given reserve token in case the reserve token got compromised
        can only be called by the changer owner
        note that selling is still enabled regardless of this flag and it cannot be disabled by the owner

        _reserveToken    reserve token contract address
        _disable         true to disable the token, false to re-enable it
    */
    function disableReserve(address _reserveToken, bool _disable)
        public
        ownerOnly
        validReserve(_reserveToken)
    {
        reserves[_reserveToken].isEnabled = !_disable;
    }

    /*
        sets the smart token's changer address to a different one instead of the current contract address
        can only be called by the owner
        the changer can be set to null to transfer ownership from the changer to the original smart token's owner

        _changer    new changer contract address (can also be set to 0x0 to remove the current changer)
    */
    function setTokenChanger(address _changer)
        public
        ownerOnly
        validAddress(_changer)
        returns (bool success)
    {
        require(_changer != address(this));
        return token.setChanger(_changer);
    }

    /*
        activates the change logic
        can only be called by the owner
        once changing is activated, it cannot be deactivated by the owner anymore
        activating the change logic also enables transfers in the smart token
    */
    function activate()
        public
        ownerOnly
        inactive
        returns (bool success)
    {
        assert(token.totalSupply() != 0 && reserveTokens.length > 0); // validate state
        token.disableTransfers(false);
        isActive = true;
        return true;
    }

    /*
        returns the expected return for changing a specific amount of _fromToken to _toToken

        _fromToken  token to change from
        _toToken    token to change to
        _amount     amount to change, in fromToken
    */
    function getReturn(address _fromToken, address _toToken, uint256 _amount)
        public
        constant
        validToken(_fromToken)
        validToken(_toToken)
        returns (uint256 amount)
    {
        require(_fromToken != _toToken); // validate input

        // change between the token and one of its reserves
        address tokenAddress = address(token);
        if (_toToken == tokenAddress)
            return getPurchaseReturn(_fromToken, _amount);
        else if (_fromToken == tokenAddress)
            return getSaleReturn(_toToken, _amount);

        // change between 2 reserves
        uint256 tempAmount = getPurchaseReturn(_fromToken, _amount);
        return getSaleReturn(_toToken, tempAmount);
    }

    /*
        returns the expected return for buying the token for a reserve token

        _reserveToken   reserve token contract address
        _depositAmount  amount to deposit (in the reserve token)
    */
    function getPurchaseReturn(address _reserveToken, uint256 _depositAmount)
        public
        constant
        active
        validReserve(_reserveToken)
        returns (uint256 amount)
    {
        Reserve reserve = reserves[_reserveToken];
        require(reserve.isEnabled && _depositAmount != 0); // validate input

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);
        assert(reserveBalance != 0); // validate state

        uint256 tokenSupply = token.totalSupply();
        return formula.calculatePurchaseReturn(tokenSupply, reserveBalance, reserve.ratio, _depositAmount);
    }

    /*
        returns the expected return for selling the token for one of its reserve tokens

        _reserveToken   reserve token contract address
        _sellAmount     amount to sell (in the smart token)
    */
    function getSaleReturn(address _reserveToken, uint256 _sellAmount)
        public
        constant
        active
        validReserve(_reserveToken)
        returns (uint256 amount)
    {
        require(_sellAmount != 0 && _sellAmount <= token.balanceOf(msg.sender)); // validate input

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);
        assert(reserveBalance != 0); // validate state

        uint256 tokenSupply = token.totalSupply();
        Reserve reserve = reserves[_reserveToken];
        return formula.calculateSaleReturn(tokenSupply, reserveBalance, reserve.ratio, _sellAmount);
    }

    /*
        changes a specific amount of _fromToken to _toToken

        _fromToken  token to change from
        _toToken    token to change to
        _amount     amount to change, in fromToken
        _minReturn  if the change results in an amount smaller than the minimum return, it is cancelled
    */
    function change(address _fromToken, address _toToken, uint256 _amount, uint256 _minReturn)
        public
        validToken(_fromToken)
        validToken(_toToken)
        returns (uint256 amount)
    {
        require(_fromToken != _toToken); // validate input

        // change between the token and one of its reserves
        address tokenAddress = address(token);
        if (_toToken == tokenAddress)
            return buy(_fromToken, _amount, _minReturn);
        else if (_fromToken == tokenAddress)
            return sell(_toToken, _amount, _minReturn);

        // change between 2 reserves
        uint256 tempAmount = buy(_fromToken, _amount, 0);
        return sell(_toToken, tempAmount, _minReturn);
    }

    /*
        buys the token by depositing one of its reserve tokens

        _reserveToken   reserve token contract address
        _depositAmount  amount to deposit (in the reserve token)
        _minReturn      if the change results in an amount smaller than the minimum return, it is cancelled
    */
    function buy(address _reserveToken, uint256 _depositAmount, uint256 _minReturn) public returns (uint256 amount) {
        amount = getPurchaseReturn(_reserveToken, _depositAmount);
        assert(amount != 0 && amount >= _minReturn); // ensure the trade gives something in return and meets the minimum requested amount

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        assert(reserveToken.transferFrom(msg.sender, this, _depositAmount)); // transfer _depositAmount funds from the caller in the reserve token

        assert(token.issue(msg.sender, amount)); // issue new funds to the caller in the smart token
        dispatchChange(_reserveToken, token, msg.sender, _depositAmount, amount);
        return amount;
    }

    /*
        sells the token by withdrawing from one of its reserve tokens

        _reserveToken   reserve token contract address
        _sellAmount     amount to sell (in the smart token)
        _minReturn      if the change results in an amount smaller the minimum return, it is cancelled
    */
    function sell(address _reserveToken, uint256 _sellAmount, uint256 _minReturn) public returns (uint256 amount) {
        amount = getSaleReturn(_reserveToken, _sellAmount);
        assert(amount != 0 && amount >= _minReturn); // ensure the trade gives something in return and meets the minimum requested amount

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);
        assert(amount <= reserveBalance); // ensure that the trade won't result in negative reserve

        uint256 tokenSupply = token.totalSupply();
        assert(amount < reserveBalance || _sellAmount == tokenSupply); // ensure that the trade will only deplete the reserve if the total supply is depleted as well

        assert(token.destroy(msg.sender, _sellAmount)); // destroy _sellAmount from the caller in the smart token
        assert(reserveToken.transfer(msg.sender, amount)); // transfer funds to the caller in the reserve token

        // if the supply was totally depleted, disconnect from the smart token
        if (_sellAmount == tokenSupply)
            token.setChanger(0x0);

        dispatchChange(this, _reserveToken, msg.sender, _sellAmount, amount);
        return amount;
    }

    // utility

    function dispatchChange(address _fromToken, address _toToken, address _trader, uint256 _amount, uint256 _return) private {
        Change(_fromToken, _toToken, _trader, _amount, _return);

        if (address(events) != 0x0)
            events.tokenChange(_fromToken, _toToken, _trader, _amount, _return);
    }

    // fallback
    function() {
        assert(false);
    }
}

pragma solidity ^0.4.10;
import './BancorEventsDispatcher.sol';
import './TokenChangerInterface.sol';
import './SmartTokenInterface.sol';

/*
    Open issues:
    - add miner abuse protection
    - assumes that the reserve tokens either return true for transfer/transferFrom or throw - possibly remove the reliance on the return value
    - the change function is wasting gas by calling getReturn - both functions initialize SmartToken/ERC20Token instances. We can save that gas
      by duplicating code but that's not ideal either
*/

// interfaces

contract BancorFormula {
    function calculatePurchaseReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _depositAmount) public constant returns (uint256 amount);
    function calculateSaleReturn(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _sellAmount) public constant returns (uint256 amount);
}

/*
    Bancor Changer v0.1
*/
contract BancorChanger is BancorEventsDispatcher, TokenChangerInterface {
    struct Reserve {
        uint8 ratio;    // constant reserve ratio (CRR), 1-100
        bool isEnabled; // is purchase of the smart token enabled with the reserve, can be set by the owner
        bool isSet;     // is the reserve set, used to tell if the mapping element is defined
    }

    SmartTokenInterface public token;               // smart token governed by the changer
    BancorFormula public formula;                   // bancor calculation formula contract
    bool public isActive = false;                   // true if the change functionality can now be used, false if not
    address[] public reserveTokens;                 // ERC20 standard token addresses
    mapping (address => Reserve) public reserves;   // reserve token addresses -> reserve data
    uint8 private totalReserveRatio = 0;            // used to prevent increasing the total reserve ratio above 100% efficiently

    // events, can be used to listen to the contract directly, as opposed to through the events contract
    event Change(address indexed _fromToken, address indexed _toToken, address indexed _trader, uint256 _amount, uint256 _return);

    /*
        _token      smart token governed by the changer
        _formula    address of a bancor formula contract
        _events     optional, address of a bancor events contract
    */
    function BancorChanger(address _token, address _formula, address _events)
        BancorEventsDispatcher(_events)
        validAddress(_token)
        validAddress(_formula)
    {
        token = SmartTokenInterface(_token);
        formula = BancorFormula(_formula);
    }

    // validates an address - currently only checks that it isn't null
    modifier validAddress(address _address) {
        assert(_address != 0x0);
        _;
    }

    // validates a reserve token address - verifies that the address belongs to one of the reserve tokens
    modifier validReserve(address _address) {
        assert(reserves[_address].isSet);
        _;
    }

    // validates a token address - verifies that the address belongs to one of the changable tokens
    modifier validToken(address _address) {
        assert(_address == address(token) || reserves[_address].isSet);
        _;
    }

    // ensures that token changing is active
    modifier active() {
        assert(isActive);
        _;
    }

    // ensures that token changing is not active
    modifier inactive() {
        assert(!isActive);
        _;
    }

    /*
        returns the number of reserve tokens defined
    */
    function reserveTokenCount() public constant returns (uint16 count) {
        return uint16(reserveTokens.length);
    }

    /*
        returns the number of changeable tokens supported by the contract
        note that the number of changable tokens is the number of reserve token, plus 1 (that represents the smart token)
    */
    function changeableTokenCount() public constant returns (uint16 count) {
        return reserveTokenCount() + 1;
    }

    /*
        given a changable token index, returns the changable token contract address
    */
    function changeableToken(uint16 _tokenIndex) public constant returns (address tokenAddress) {
        if (_tokenIndex == 0)
            return token;
        return reserveTokens[_tokenIndex - 1];
    }

    /*
        defines a new reserve for the token
        can only be called by the changer owner while the changer is inactive

        _token  address of the reserve token
        _ratio  constant reserve ratio, 1-100
    */
    function addReserve(address _token, uint8 _ratio)
        public
        ownerOnly
        inactive
        validAddress(_token)
        returns (bool success)
    {
        require(_token != address(this) && _token != address(token) && !reserves[_token].isSet && _ratio > 0 && _ratio <= 100 && totalReserveRatio + _ratio <= 100); // validate input

        reserves[_token].ratio = _ratio;
        reserves[_token].isEnabled = true;
        reserves[_token].isSet = true;
        reserveTokens.push(_token);
        totalReserveRatio += _ratio;
        return true;
    }

    /*
        allows the owner to execute the smart token's issue function

        _to         account to receive the new amount
        _amount     amount to increase the supply by
    */
    function issueToken(address _to, uint256 _amount) public ownerOnly returns (bool success) {
        return token.issue(_to, _amount);
    }

    /*
        allows the owner to execute the smart token's destroy function

        _from       account to remove the new amount from
        _amount     amount to decrease the supply by
    */
    function destroyToken(address _from, uint256 _amount) public ownerOnly returns (bool success) {
        return token.destroy(_from, _amount);
    }

    /*
        withdraws tokens from the reserve and sends them to an account
        can only be called by the changer owner while the changer is inactive

        _reserveToken    reserve token contract address
        _to              account to receive the new amount
        _amount          amount to withdraw (in the reserve token)
    */
    function withdraw(address _reserveToken, address _to, uint256 _amount)
        public
        ownerOnly
        validReserve(_reserveToken)
        validAddress(_to)
        returns (bool success)
    {
        require(_to != address(this) && _amount != 0); // validate input
        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        return reserveToken.transfer(_to, _amount);
    }

    /*
        disables purchasing with the given reserve token in case the reserve token got compromised
        can only be called by the changer owner
        note that selling is still enabled regardless of this flag and it cannot be disabled by the owner

        _reserveToken    reserve token contract address
        _disable         true to disable the token, false to re-enable it
    */
    function disableReserve(address _reserveToken, bool _disable)
        public
        ownerOnly
        validReserve(_reserveToken)
    {
        reserves[_reserveToken].isEnabled = !_disable;
    }

    /*
        sets the smart token's changer address to a different one instead of the current contract address
        can only be called by the owner
        the changer can be set to null to transfer ownership from the changer to the original smart token's owner

        _changer    new changer contract address (can also be set to 0x0 to remove the current changer)
    */
    function setTokenChanger(address _changer)
        public
        ownerOnly
        validAddress(_changer)
        returns (bool success)
    {
        require(_changer != address(this));
        return token.setChanger(_changer);
    }

    /*
        activates the change logic
        can only be called by the owner
        once changing is activated, it cannot be deactivated by the owner anymore
        activating the change logic also enables transfers in the smart token
    */
    function activate()
        public
        ownerOnly
        inactive
        returns (bool success)
    {
        assert(token.totalSupply() != 0 && reserveTokens.length > 0); // validate state
        token.disableTransfers(false);
        isActive = true;
        return true;
    }

    /*
        returns the expected return for changing a specific amount of _fromToken to _toToken

        _fromToken  token to change from
        _toToken    token to change to
        _amount     amount to change, in fromToken
    */
    function getReturn(address _fromToken, address _toToken, uint256 _amount)
        public
        constant
        validToken(_fromToken)
        validToken(_toToken)
        returns (uint256 amount)
    {
        require(_fromToken != _toToken); // validate input

        // change between the token and one of its reserves
        address tokenAddress = address(token);
        if (_toToken == tokenAddress)
            return getPurchaseReturn(_fromToken, _amount);
        else if (_fromToken == tokenAddress)
            return getSaleReturn(_toToken, _amount);

        // change between 2 reserves
        uint256 tempAmount = getPurchaseReturn(_fromToken, _amount);
        return getSaleReturn(_toToken, tempAmount);
    }

    /*
        returns the expected return for buying the token for a reserve token

        _reserveToken   reserve token contract address
        _depositAmount  amount to deposit (in the reserve token)
    */
    function getPurchaseReturn(address _reserveToken, uint256 _depositAmount)
        public
        constant
        active
        validReserve(_reserveToken)
        returns (uint256 amount)
    {
        Reserve reserve = reserves[_reserveToken];
        require(reserve.isEnabled && _depositAmount != 0); // validate input

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);
        assert(reserveBalance != 0); // validate state

        uint256 tokenSupply = token.totalSupply();
        return formula.calculatePurchaseReturn(tokenSupply, reserveBalance, reserve.ratio, _depositAmount);
    }

    /*
        returns the expected return for selling the token for one of its reserve tokens

        _reserveToken   reserve token contract address
        _sellAmount     amount to sell (in the smart token)
    */
    function getSaleReturn(address _reserveToken, uint256 _sellAmount)
        public
        constant
        active
        validReserve(_reserveToken)
        returns (uint256 amount)
    {
        require(_sellAmount != 0 && _sellAmount <= token.balanceOf(msg.sender)); // validate input

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);
        assert(reserveBalance != 0); // validate state

        uint256 tokenSupply = token.totalSupply();
        Reserve reserve = reserves[_reserveToken];
        return formula.calculateSaleReturn(tokenSupply, reserveBalance, reserve.ratio, _sellAmount);
    }

    /*
        changes a specific amount of _fromToken to _toToken

        _fromToken  token to change from
        _toToken    token to change to
        _amount     amount to change, in fromToken
        _minReturn  if the change results in an amount smaller than the minimum return, it is cancelled
    */
    function change(address _fromToken, address _toToken, uint256 _amount, uint256 _minReturn)
        public
        validToken(_fromToken)
        validToken(_toToken)
        returns (uint256 amount)
    {
        require(_fromToken != _toToken); // validate input

        // change between the token and one of its reserves
        address tokenAddress = address(token);
        if (_toToken == tokenAddress)
            return buy(_fromToken, _amount, _minReturn);
        else if (_fromToken == tokenAddress)
            return sell(_toToken, _amount, _minReturn);

        // change between 2 reserves
        uint256 tempAmount = buy(_fromToken, _amount, 0);
        return sell(_toToken, tempAmount, _minReturn);
    }

    /*
        buys the token by depositing one of its reserve tokens

        _reserveToken   reserve token contract address
        _depositAmount  amount to deposit (in the reserve token)
        _minReturn      if the change results in an amount smaller than the minimum return, it is cancelled
    */
    function buy(address _reserveToken, uint256 _depositAmount, uint256 _minReturn) public returns (uint256 amount) {
        amount = getPurchaseReturn(_reserveToken, _depositAmount);
        assert(amount != 0 && amount >= _minReturn); // ensure the trade gives something in return and meets the minimum requested amount

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        assert(reserveToken.transferFrom(msg.sender, this, _depositAmount)); // transfer _depositAmount funds from the caller in the reserve token

        assert(token.issue(msg.sender, amount)); // issue new funds to the caller in the smart token
        dispatchChange(_reserveToken, token, msg.sender, _depositAmount, amount);
        return amount;
    }

    /*
        sells the token by withdrawing from one of its reserve tokens

        _reserveToken   reserve token contract address
        _sellAmount     amount to sell (in the smart token)
        _minReturn      if the change results in an amount smaller the minimum return, it is cancelled
    */
    function sell(address _reserveToken, uint256 _sellAmount, uint256 _minReturn) public returns (uint256 amount) {
        amount = getSaleReturn(_reserveToken, _sellAmount);
        assert(amount != 0 && amount >= _minReturn); // ensure the trade gives something in return and meets the minimum requested amount

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);
        assert(amount <= reserveBalance); // ensure that the trade won't result in negative reserve

        uint256 tokenSupply = token.totalSupply();
        assert(amount < reserveBalance || _sellAmount == tokenSupply); // ensure that the trade will only deplete the reserve if the total supply is depleted as well

        assert(token.destroy(msg.sender, _sellAmount)); // destroy _sellAmount from the caller in the smart token
        assert(reserveToken.transfer(msg.sender, amount)); // transfer funds to the caller in the reserve token

        // if the supply was totally depleted, disconnect from the smart token
        if (_sellAmount == tokenSupply)
            token.setChanger(0x0);

        dispatchChange(this, _reserveToken, msg.sender, _sellAmount, amount);
        return amount;
    }

    // utility

    function dispatchChange(address _fromToken, address _toToken, address _trader, uint256 _amount, uint256 _return) private {
        Change(_fromToken, _toToken, _trader, _amount, _return);

        if (address(events) != 0x0)
            events.tokenChange(_fromToken, _toToken, _trader, _amount, _return);
    }

    // fallback
    function() {
        assert(false);
    }
}


Remove LinkedList

Remove LinkedList

/*
 * Third-party implementation by Eris Ltd.
 * src:
 */

/* TODO index on `address` type? */
contract DoublyLinkedList {

  struct Element {
    address previous;
    address next;

    bytes32 data;
  }

  // Make these public.
  uint public size;
  address public tail;
  address public head;
  mapping(address => Element) elements;

  function getData(address key) returns (bytes32){
    return elements[key].data;
  }

  function getElement(address key) constant returns (address prev, address next, bytes32 elemData){
      return (elements[key].previous, elements[key].next, elements[key].data);
  }

  function addElement(address key, bytes32 data) returns (bool){
    Element elem = elements[key];
    // Check that the key is not already taken. We have no null-check for structs atm., so
    // we need to check the fields inside the structs to verify. This works if the field we
    // check is not allowed to be null (which would be 0 or 0x0 in the case of addresses).
    if(elem.data != ""){
      return false;
    }

    elem.data = data;

      // Two cases - empty or not.
      if(size == 0){
        tail = key;
        head = key;
      } else {
        // Link
        elements[head].next = key;
        elem.previous = head;
        // Set this element as the new head.
        head = key;
      }
      // Regardless of case, increase the size of the list by one.
      size++;
       return true;
  }

    function removeElement(address key) returns (bool result) {

       Element elem = elements[key];

      // If no element - return false. Nothing to remove.
      if(elem.data == ""){
        return false;
      }

    // If this is the only element.
      if(size == 1){
        tail = 0x0;
        head = 0x0;
      // If this is the head.
      } else if (key == head){
        // Set this ones 'previous' to be the new head, then change its
        // next to be null (used to be this one).
        head = elem.previous;
        elements[head].next = 0x0;
      // If this one is the tail.
      } else if(key == tail){
        tail = elem.next;
        elements[tail].previous = 0x0;
      // Now it's a bit tougher. Getting here means the list has at least 3 elements,
      // and this element must have both a 'previous' and a 'next'.
      } else {
        address prevElem = elem.previous;
        address nextElem = elem.next;
        elements[prevElem].next = nextElem;
        elements[nextElem].previous = prevElem;
      }
      // Regardless of case, we will decrease the list size by 1, and delete the actual entry.
      size--;
      delete elements[key];
      return true;
    }

}


improvements

improvements

pragma solidity 0.4.15;

import './ownership/multiowned.sol';
import './crowdsale/FixedTimeBonuses.sol';
import './crowdsale/FundsRegistry.sol';
import './STQToken.sol';
import 'zeppelin-solidity/contracts/ReentrancyGuard.sol';
import 'zeppelin-solidity/contracts/math/Math.sol';
import 'zeppelin-solidity/contracts/math/SafeMath.sol';

/// @title Storiqa ICO contract
contract STQCrowdsale is multiowned, ReentrancyGuard {
    using Math for uint256;
    using SafeMath for uint256;
    using FixedTimeBonuses for FixedTimeBonuses.Data;

    uint internal constant MSK2UTC_DELTA = 3600 * 3;

    enum IcoState { INIT, ICO, PAUSED, FAILED, SUCCEEDED }

    event StateChanged(IcoState _state);

    modifier requiresState(IcoState _state) {
        require(m_state == _state);
        _;
    }

    /// @dev triggers some state changes based on current time
    /// note: function body could be skipped!
    modifier timedStateChange() {
        if (IcoState.INIT == m_state && getCurrentTime() >= getStartTime())
            changeState(IcoState.ICO);

        if (IcoState.ICO == m_state && getCurrentTime() > getEndTime()) {
            finishICO();

            if (msg.value > 0)
                msg.sender.transfer(msg.value);
        } else {
            _;
        }
    }

    /// @dev automatic check for unaccounted withdrawals
    modifier fundsChecker() {
        assert(m_state == IcoState.ICO);

        uint atTheBeginning = m_funds.balance;
        if (atTheBeginning < m_lastFundsAmount) {
            changeState(IcoState.PAUSED);
            if (msg.value > 0)
                msg.sender.transfer(msg.value); // we cant throw (have to save state), so refunding this way
        } else {
            _;

            if (m_funds.balance < atTheBeginning) {
                changeState(IcoState.PAUSED);
            } else {
                m_lastFundsAmount = m_funds.balance;
            }
        }
    }

    // PUBLIC interface

    function STQCrowdsale(address[] _owners, address _token, address _funds)
        multiowned(_owners, 2)
    {
        require(3 == _owners.length);
        require(address(0) != address(_token) && address(0) != address(_funds));

        m_token = STQToken(_token);
        m_funds = FundsRegistry(_funds);

        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1505768399 + MSK2UTC_DELTA, bonus: 25}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1505941199 + MSK2UTC_DELTA, bonus: 20}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506200399 + MSK2UTC_DELTA, bonus: 15}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506545999 + MSK2UTC_DELTA, bonus: 10}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506891599 + MSK2UTC_DELTA, bonus: 5}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1508360399 + MSK2UTC_DELTA, bonus: 0}));
        m_bonuses.validate(true);
    }

    // PUBLIC interface: payments

    // fallback function as a shortcut
    function() payable {
        buy();  // only internal call here!
    }

    /// @notice ICO participation
    /// @return number of STQ tokens bought (with all decimal symbols)
    function buy()
        public
        payable
        nonReentrant
        timedStateChange
        requiresState(IcoState.ICO)
        fundsChecker
        returns (uint)
    {
        address investor = msg.sender;
        uint256 payment = msg.value;
        require(payment >= c_MinInvestment);

        uint startingInvariant = this.balance.add(m_funds.balance);

        // checking for max cap
        uint fundsAllowed = getMaximumFunds().sub(m_funds.totalInvested());
        assert(0 != fundsAllowed);  // in this case state must not be IcoState.ICO
        payment = fundsAllowed.min256(payment);
        uint256 change = msg.value.sub(payment);

        // issue tokens
        uint stq = calcSTQAmount(payment);
        m_token.mint(investor, stq);

        // record payment
        m_funds.invested.value(payment)(investor);

        // check if ICO must be closed early
        if (change > 0)
        {
            assert(getMaximumFunds() == m_funds.totalInvested());
            finishICO();

            // send change
            investor.transfer(change);
            assert(startingInvariant == this.balance.add(m_funds.balance).add(change));
        }
        else
            assert(startingInvariant == this.balance.add(m_funds.balance));

        return stq;
    }

    // PUBLIC interface: owners: maintenance

    /// @notice pauses ICO
    function pause()
        external
        timedStateChange
        requiresState(IcoState.ICO)
        onlyowner
    {
        changeState(IcoState.PAUSED);
    }

    /// @notice resume paused ICO
    function unpause()
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        changeState(IcoState.ICO);
        checkTime();
    }

    /// @notice consider paused ICO as failed
    function fail()
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        changeState(IcoState.FAILED);
    }

    /// @notice In case we need to attach to existent token
    function setToken(address _token)
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        require(address(0) != _token);
        m_token = STQToken(_token);
    }

    /// @notice In case we need to attach to existent funds
    function setFundsRegistry(address _funds)
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        require(address(0) != _funds);
        m_funds = FundsRegistry(_funds);
    }

    /// @notice explicit trigger for timed state changes
    function checkTime()
        public
        timedStateChange
        onlyowner
    {
    }

    // INTERNAL functions

    function finishICO() private {
        if (m_funds.totalInvested() < getMinFunds())
            changeState(IcoState.FAILED);
        else
            changeState(IcoState.SUCCEEDED);
    }

    /// @dev performs only allowed state transitions
    function changeState(IcoState _newState) private {
        assert(m_state != _newState);

        if (IcoState.INIT == m_state) {        assert(IcoState.ICO == _newState); }
        else if (IcoState.ICO == m_state) {    assert(IcoState.PAUSED == _newState || IcoState.FAILED == _newState || IcoState.SUCCEEDED == _newState); }
        else if (IcoState.PAUSED == m_state) { assert(IcoState.ICO == _newState || IcoState.FAILED == _newState); }
        else assert(false);

        m_state = _newState;
        StateChanged(m_state);

        // this should be tightly linked
        if (IcoState.SUCCEEDED == m_state) {
            onSuccess();
        } else if (IcoState.FAILED == m_state) {
            onFailure();
        }
    }

    function onSuccess() private {
        // mint tokens for owners
        uint tokensPerOwner = m_token.totalSupply().mul(4).div(m_numOwners);
        for (uint i = 0; i < m_numOwners; i++)
            m_token.mint(getOwner(i), tokensPerOwner);

        m_funds.changeState(FundsRegistry.State.SUCCEEDED);
        m_funds.detachController();

        m_token.disableMinting();
        m_token.startCirculation();
        m_token.detachController();
    }

    function onFailure() private {
        m_funds.changeState(FundsRegistry.State.REFUNDING);
        m_funds.detachController();
    }

    /// @dev calculates amount of STQ to which payer of _wei is entitled
    function calcSTQAmount(uint _wei) private constant returns (uint) {
        uint stq = _wei.mul(c_STQperETH);

        // apply bonus
        stq = stq.mul(m_bonuses.getBonus(getCurrentTime()).add(100)).div(100);

        return stq;
    }

    /// @dev start time of the ICO, inclusive
    function getStartTime() private constant returns (uint) {
        return c_startTime;
    }

    /// @dev end time of the ICO, inclusive
    function getEndTime() private constant returns (uint) {
        return m_bonuses.getLastTime();
    }

    /// @dev to be overridden in tests
    function getCurrentTime() internal constant returns (uint) {
        return now;
    }

    /// @dev to be overridden in tests
    function getMinFunds() internal constant returns (uint) {
        return c_MinFunds;
    }

    /// @dev to be overridden in tests
    function getMaximumFunds() internal constant returns (uint) {
        return c_MaximumFunds;
    }

    // FIELDS

    /// @notice starting exchange rate of STQ
    uint public constant c_STQperETH = 100;

    /// @notice minimum investment
    uint public constant c_MinInvestment = 10 finney;

    /// @notice minimum investments to consider ICO as a success
    uint public constant c_MinFunds = 1000 ether;

    /// @notice maximum investments to be accepted during ICO
    uint public constant c_MaximumFunds = 500000 ether;

    /// @notice start time of the ICO
    uint public constant c_startTime = 1505682000 + MSK2UTC_DELTA;

    /// @notice timed bonuses
    FixedTimeBonuses.Data m_bonuses;

    /// @dev state of the ICO
    IcoState public m_state = IcoState.INIT;

    /// @dev contract responsible for token accounting
    STQToken public m_token;

    /// @dev contract responsible for investments accounting
    FundsRegistry public m_funds;

    /// @dev last recorded funds
    uint256 public m_lastFundsAmount;
}

pragma solidity 0.4.15;

import './ownership/multiowned.sol';
import './crowdsale/FixedTimeBonuses.sol';
import './crowdsale/FundsRegistry.sol';
import './STQToken.sol';
import 'zeppelin-solidity/contracts/ReentrancyGuard.sol';
import 'zeppelin-solidity/contracts/math/Math.sol';
import 'zeppelin-solidity/contracts/math/SafeMath.sol';

/// @title Storiqa ICO contract
// FIXME WARNING: dont use it, it was't audited yet
contract STQCrowdsale is multiowned, ReentrancyGuard {
    using Math for uint256;
    using SafeMath for uint256;
    using FixedTimeBonuses for FixedTimeBonuses.Data;

    uint internal constant MSK2UTC_DELTA = 3600 * 3;

    enum IcoState { INIT, ICO, PAUSED, FAILED, SUCCEEDED }

    event StateChanged(IcoState _state);

    modifier requiresState(IcoState _state) {
        require(m_state == _state);
        _;
    }

    /// @dev triggers some state changes based on current time
    /// note: function body could be skipped!
    modifier timedStateChange() {
        if (IcoState.INIT == m_state && getCurrentTime() >= getStartTime())
            changeState(IcoState.ICO);

        if (IcoState.ICO == m_state && getCurrentTime() > getEndTime()) {
            if (msg.value > 0)
                msg.sender.transfer(msg.value);

            finishICO();
        } else {
            _;
        }
    }

    /// @dev automatic check for unaccounted withdrawals
    modifier fundsChecker() {
        assert(m_state == IcoState.ICO);

        uint atTheBeginning = m_funds.balance;
        if (atTheBeginning < m_lastFundsAmount) {
            changeState(IcoState.PAUSED);
            if (msg.value > 0)
                msg.sender.transfer(msg.value); // we cant throw (have to save state), so refunding this way
        } else {
            _;

            if (m_funds.balance < atTheBeginning) {
                changeState(IcoState.PAUSED);
            } else {
                m_lastFundsAmount = m_funds.balance;
            }
        }
    }

    // PUBLIC interface

    function STQCrowdsale(address[] _owners, address _token, address _funds)
        multiowned(_owners, 2)
    {
        require(3 == _owners.length);
        require(address(0) != address(_token) && address(0) != address(_funds));

        m_token = STQToken(_token);
        m_funds = FundsRegistry(_funds);

        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1505768399 + MSK2UTC_DELTA, bonus: 25}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1505941199 + MSK2UTC_DELTA, bonus: 20}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506200399 + MSK2UTC_DELTA, bonus: 15}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506545999 + MSK2UTC_DELTA, bonus: 10}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1506891599 + MSK2UTC_DELTA, bonus: 5}));
        m_bonuses.bonuses.push(FixedTimeBonuses.Bonus({endTime: 1508360399 + MSK2UTC_DELTA, bonus: 0}));
        m_bonuses.validate(true);
    }

    // PUBLIC interface: payments

    // fallback function as a shortcut
    function() payable {
        buy();  // only internal call here!
    }

    /// @notice ICO participation
    /// @return number of STQ tokens bought (with all decimal symbols)
    function buy()
        public
        payable
        nonReentrant
        timedStateChange
        requiresState(IcoState.ICO)
        fundsChecker
        returns (uint)
    {
        address investor = msg.sender;
        uint256 payment = msg.value;
        require(payment >= c_MinInvestment);

        uint startingInvariant = this.balance.add(m_funds.balance);

        // checking for max cap
        uint fundsAllowed = getMaximumFunds().sub(m_funds.totalInvested());
        assert(0 != fundsAllowed);  // in this case state must not be IcoState.ICO
        payment = fundsAllowed.min256(payment);
        uint256 change = msg.value.sub(payment);

        // issue tokens
        uint stq = calcSTQAmount(payment);
        m_token.mint(investor, stq);

        // record payment
        m_funds.invested.value(payment)(investor);

        // check if ICO must be closed early
        if (change > 0)
        {
            assert(getMaximumFunds() == m_funds.totalInvested());
            finishICO();

            // send change
            investor.transfer(change);
            assert(startingInvariant == this.balance.add(m_funds.balance).add(change));
        }
        else
            assert(startingInvariant == this.balance.add(m_funds.balance));

        return stq;
    }

    // PUBLIC interface: owners: maintenance

    /// @notice pauses ICO
    function pause()
        external
        timedStateChange
        requiresState(IcoState.ICO)
        onlyowner
    {
        changeState(IcoState.PAUSED);
    }

    /// @notice resume paused ICO
    function unpause()
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        changeState(IcoState.ICO);
        checkTime();
    }

    /// @notice consider paused ICO as failed
    function fail()
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        changeState(IcoState.FAILED);
    }

    /// @notice In case we need to attach to existent token
    function setToken(address _token)
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        require(address(0) != _token);
        m_token = STQToken(_token);
    }

    /// @notice In case we need to attach to existent funds
    function setFundsRegistry(address _funds)
        external
        timedStateChange
        requiresState(IcoState.PAUSED)
        onlymanyowners(sha3(msg.data))
    {
        require(address(0) != _funds);
        m_funds = FundsRegistry(_funds);
    }

    /// @notice explicit trigger for timed state changes
    function checkTime()
        public
        timedStateChange
        onlyowner
    {
    }

    // INTERNAL functions

    function finishICO() private {
        if (m_funds.totalInvested() < getMinFunds())
            changeState(IcoState.FAILED);
        else
            changeState(IcoState.SUCCEEDED);
    }

    /// @dev performs only allowed state transitions
    function changeState(IcoState _newState) private {
        assert(m_state != _newState);

        if (IcoState.INIT == m_state) {        assert(IcoState.ICO == _newState); }
        else if (IcoState.ICO == m_state) {    assert(IcoState.PAUSED == _newState || IcoState.FAILED == _newState || IcoState.SUCCEEDED == _newState); }
        else if (IcoState.PAUSED == m_state) { assert(IcoState.ICO == _newState || IcoState.FAILED == _newState); }
        else assert(false);

        m_state = _newState;
        StateChanged(m_state);

        // this should be tightly linked
        if (IcoState.SUCCEEDED == m_state) {
            onSuccess();
        } else if (IcoState.FAILED == m_state) {
            onFailure();
        }
    }

    function onSuccess() private {
        // mint tokens for owners
        uint tokensPerOwner = m_token.totalSupply().mul(4).div(m_numOwners);
        for (uint i = 0; i < m_numOwners; i++)
            m_token.mint(getOwner(i), tokensPerOwner);

        m_funds.changeState(FundsRegistry.State.SUCCEEDED);
        m_funds.detachController();

        m_token.disableMinting();
        m_token.startCirculation();
        m_token.detachController();
    }

    function onFailure() private {
        m_funds.changeState(FundsRegistry.State.REFUNDING);
        m_funds.detachController();
    }

    /// @dev calculates amount of STQ to which payer of _wei is entitled
    function calcSTQAmount(uint _wei) private constant returns (uint) {
        uint stq = _wei.mul(c_STQperETH);

        // apply bonus
        stq = stq.mul(m_bonuses.getBonus(getCurrentTime()).add(100)).div(100);

        return stq;
    }

    /// @dev start time of the ICO, inclusive
    function getStartTime() private constant returns (uint) {
        return c_startTime;
    }

    /// @dev end time of the ICO, inclusive
    function getEndTime() private constant returns (uint) {
        return m_bonuses.getLastTime();
    }

    /// @dev to be overridden in tests
    function getCurrentTime() internal constant returns (uint) {
        return now;
    }

    /// @dev to be overridden in tests
    function getMinFunds() internal constant returns (uint) {
        return c_MinFunds;
    }

    /// @dev to be overridden in tests
    function getMaximumFunds() internal constant returns (uint) {
        return c_MaximumFunds;
    }

    // FIELDS

    /// @notice starting exchange rate of STQ
    uint public constant c_STQperETH = 100;

    /// @notice minimum investment
    uint public constant c_MinInvestment = 10 finney;

    /// @notice minimum investments to consider ICO as a success
    uint public constant c_MinFunds = 1000 ether;

    /// @notice maximum investments to be accepted during ICO
    uint public constant c_MaximumFunds = 500000 ether;

    /// @notice start time of the ICO
    uint public constant c_startTime = 1505682000 + MSK2UTC_DELTA;

    /// @notice timed bonuses
    FixedTimeBonuses.Data m_bonuses;

    /// @dev state of the ICO
    IcoState public m_state = IcoState.INIT;

    /// @dev contract responsible for token accounting
    STQToken public m_token;

    /// @dev contract responsible for investments accounting
    FundsRegistry public m_funds;

    /// @dev last recorded funds
    uint256 public m_lastFundsAmount;
}


Comment added

Comment added

contract PlutusDex {
    enum CurrencySymbol { EUR, GBP, USD }
    //Addresses of ethereum accounts that are approved to verify bitcoin transactions,
    //, verify/perform deposit of fiat to escrow account and verify/perform deposit
    //of fiat to vdc
    mapping(address => bool) approvedTraders;

    modifier aprrovedTrader() {
      if(approvedTraders[msg.sender]) _
    }

    struct FiatDeposit {
        address trader;
        uint fiatDeposited;
        CurrencySymbol fiatSymbol;
        uint btcAsked;
        //From https://forum.ethereum.org/discussion/3139/store-bitcoin-address-in-smart-contract
        bytes20 btcAddress; //Public address to deposit the bitcoin too
    }

    mapping(address => FiatDeposit) fiatDeposits;

    uint btcTradingVolume;

    event FiatDeposited(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress);

    event VdcLoaded(bytes32 userVdcIban, uint fiatAmount, CurrencySymbol fiatSymbol, uint btcTradingVolume);

    function PlutusDex() {
        approvedTraders[msg.sender] = true;
        btcTradingVolume = 0;
    }

    function depositFiat(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress) aprrovedTrader {
        fiatDeposits[trader] = FiatDeposit(trader, fiatDeposited, fiatSymbol, btcAsked, btcAddress);
        //Emit event, to notify all plutus-users
        FiatDeposited(trader, fiatDeposited, fiatSymbol, btcAsked, btcAddress);
    }

    /**
     * Offer amount of BTC to specified trader.
     * Handling of cheapest price should all be done offchain.
     * /
    function offerBtc(address trader, uint btcOffered, bytes32 userVdcIban) aprrovedTrader returns(bool result) {
        //TODO some Null check
        FiatDeposit deposited = fiatDeposits[trader];
        uint btcTraded;
        if (deposited.btcAsked < btcOffered) {
            btcTraded = btcOffered;
        } else {
            btcTraded = deposited.btcAsked;
        }
        uint fiatReceived = deposited.fiatDeposited * (btcTraded / btcOffered);
        deposited.btcAsked -= btcTraded;
        deposited.fiatDeposited -= fiatReceived;
        btcTradingVolume += btcTraded;
        //Notify user
        VdcLoaded(userVdcIban, fiatReceived, deposited.fiatSymbol, btcTradingVolume);
        return true;
    }
}

contract PlutusDex {
    enum CurrencySymbol { EUR, GBP, USD }
    //Addresses of ethereum accounts that are approved to verify bitcoin transactions,
    //, verify/perform deposit of fiat to escrow account and verify/perform deposit
    //of fiat to vdc
    mapping(address => bool) approvedTraders;

    modifier aprrovedTrader() {
      if(approvedTraders[msg.sender]) _
    }

    struct FiatDeposit {
        address trader;
        uint fiatDeposited;
        CurrencySymbol fiatSymbol;
        uint btcAsked;
        //From https://forum.ethereum.org/discussion/3139/store-bitcoin-address-in-smart-contract
        bytes20 btcAddress; //Public address to deposit the bitcoin too
    }

    mapping(address => FiatDeposit) fiatDeposits;

    uint btcTradingVolume;

    event FiatDeposited(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress);

    event VdcLoaded(bytes32 userVdcIban, uint fiatAmount, CurrencySymbol fiatSymbol, uint btcTradingVolume);

    function PlutusDex() {
        approvedTraders[msg.sender] = true;
        btcTradingVolume = 0;
    }

    function depositFiat(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress) aprrovedTrader {
        fiatDeposits[trader] = FiatDeposit(trader, fiatDeposited, fiatSymbol, btcAsked, btcAddress);
        //Emit event, to notify all plutus-users
        FiatDeposited(trader, fiatDeposited, fiatSymbol, btcAsked, btcAddress);
    }

    function offerBtc(address trader, uint btcOffered, bytes32 userVdcIban) aprrovedTrader returns(bool result) {
        //TODO some Null check
        FiatDeposit deposited = fiatDeposits[trader];
        uint btcTraded;
        if (deposited.btcAsked < btcOffered) {
            btcTraded = btcOffered;
        } else {
            btcTraded = deposited.btcAsked;
        }
        uint fiatReceived = deposited.fiatDeposited * (btcTraded / btcOffered);
        deposited.btcAsked -= btcTraded;
        deposited.fiatDeposited -= fiatReceived;
        btcTradingVolume += btcTraded;
        //Notify user
        VdcLoaded(userVdcIban, fiatReceived, deposited.fiatSymbol, btcTradingVolume);
        return true;
    }
}


Initialize lastPrice use divisor constant

pragma solidity ^0.4.8;

//This contract is backed by the constitution of SuperDAO deployed at : .
//The constitution of the superdao is the social contract, terms, founding principles and definitions of the vision,
//mission, anti-missions, rules and operation guidelines of Superdao.
//The total number of 3,000,000 represents 3% of 100,000,000 immutable number of superdao tokens,
//which is the alloted budget of operation for the earliest funding activities.
//Every prommissory token is exchangeable for the real SuperDAO tokens on a one on one basis.
//Promissiory contract will be deployed with the actual superdao token contract.
//Early backers can call the "redeem" function on the actual token contract to exchange promissory tokens for the final tokens.

/**
 * @title Promisory Token Contract
 * @author ola
 * --- Collaborators ---
 * @author zlatinov
 * @author panos
 * @author yemi
 * @author archil
 * @author anthony
 */
contract PromissoryToken {

    event FounderSwitchRequestEvent(address _newFounderAddr);
    event FounderSwitchedEvent(address _newFounderAddr);
    event CofounderSwitchedEvent(address _newCofounderAddr);

    event AddedPrepaidTokensEvent(address backer, uint index, uint price, uint amount);
    event PrepaidTokensClaimedEvent(address backer, uint index, uint price, uint amount);
    event TokensClaimedEvent(address backer, uint index, uint price, uint amount);

    event RedeemEvent(address backer, uint amount);

    event WithdrawalCreatedEvent(uint withdrawalId, uint amount, bytes reason);
    event WithdrawalVotedEvent(uint withdrawalId, address backer, uint backerStakeWeigth, uint totalStakeWeight);
    event WithdrawalApproved(uint withdrawalId, uint stakeWeight, bool isMultiPayment, uint amount, bytes reason);

    address founder; //deployer of constitution and PromissoryToken
    bytes32 founderHash; // hash must be confirmed in order to replace founder address
    mapping(address => bytes32) tempHashes; // structure to contain new address to hash storage,
    address cofounder;//helper to aid founder key exchange in case of key loss
    address [] public previousFounders; //list of addresses replaced using the switching process.
    uint constant discountAmount = 60; //discount amount
    uint constant divisor = 100; //divisor to get discount value

    uint public constant minimumPrepaidClaimedPercent = 65;
    uint public promissoryUnits = 3000000; //amount of tokens contants set
    uint public prepaidUnits = 0; //prepaid and set by founder out of 3 million tokens
    uint public claimedUnits = 0; //claimed tokens out of 3 million tokens
    uint public claimedPrepaidUnits = 0; //claimed tokens out of the early backer's tokens/prepaidUnits
    uint public redeemedTokens = 0; //number of tokens out of claimed tokens, redeemed by superdao token call
    uint public lastPrice = 0; //latest price of token acquired by backer in Wei
    uint public numOfBackers; //number of early backers

    struct backerData {
       uint tokenPrice;
       uint tokenAmount;
       bytes32 privateHash;
       bool prepaid;
       bool claimed;
       uint backerRank;
    }

    address[] public earlyBackerList; //addresses of earliest backers
    address[] public backersAddresses; //addresses of all backers
    mapping(address => backerData[]) public backers;// backer address to backer info mapping
    mapping(address => bool) public backersRedeemed;

    struct withdrawalData {
       uint Amount;
       bool approved;
       bool spent;
       bytes reason;
       address[] backerApprovals;
       uint totalStake;
       address[] destination;
    }

    withdrawalData[] public withdrawals; // Data structure specifying withdrawal
    mapping(address => mapping(uint => bool)) public withdrawalsVotes;

    /**
    * @notice Deploy PromissoryToken contract with `msg.sender.address()` as founder with `_prepaidBackers.number()` prepaid backers
    * @dev This is the constructor of the promisory token contract
    * @param _founderHash Founders password hash, preferable a message digest to further obfuscate duplicaion
    * @param _cofounderAddress The helper cofounder to aid founder key exchange in case of key loss/
    * @param _numOfBackers The number of Early backers. Will be used to control setting early backers
    */
    function PromissoryToken( bytes32 _founderHash, address _cofounderAddress, uint _numOfBackers){
        founder = msg.sender;
        founderHash = sha3(_founderHash);
        cofounder = _cofounderAddress;
        numOfBackers = _numOfBackers;
    }

    /**
    * @notice `msg.sender.address()` updating cofounder address to `_newFounderAddr.address()`
    * @dev allows cofounder to switch out addres for a new one.Can be repeated as many times as needed
    * @param _newCofounderAddr New Address of Cofounder
    * @return True if the coFounder address successfully updated
    */
    function cofounderSwitchAddress(address _newCofounderAddr) external returns (bool success){
        if (msg.sender != cofounder) throw;

        cofounder = _newCofounderAddr;
        CofounderSwitchedEvent(_newCofounderAddr);

        return true;
    }

    /**
    * @notice Founder address update to `_newFounderAddr.address()` is being requested
    * @dev founderSwitchAddress founder indicates intent to switch addresses with new address,
    * hash of pass phrase and a "onetime shared phrase shared with coufounder"
    * @param _founderHash Secret Key to be used to confirm Address update
    * @param _oneTimesharedPhrase Secret phrase to be shared with coFounder to approve Address update
    * @return True if Address switch request successfully created and Temporary hash Values set
    */
    function founderSwitchRequest(bytes32 _founderHash, bytes32 _oneTimesharedPhrase) returns (bool success){
        if(sha3(_founderHash) != founderHash) throw;

        tempHashes[msg.sender] = sha3(msg.sender, founderHash, _oneTimesharedPhrase);
        FounderSwitchRequestEvent(msg.sender);

        return true;
    }

   /**
    * @notice `msg.sender.address()` approving `_newFounderAddr.address()` as new founder address
    * @dev CofounderSwitchAddress which allows previously set cofounder to approve address
    * switch by founder. Must have a one time shared phrase thats is shared with founder that corresponding with a
    * hashed value.
    * @param _newFounderAddr The address of Founder to be newly set
    * @param _oneTimesharedPhrase Secret Keyword to be provide access to the approval function
    * @return True if new Founder address successfully approved
    */
    function cofounderApproveSwitchRequest(address _newFounderAddr, bytes32 _oneTimesharedPhrase) external returns (bool success){
        if(msg.sender != cofounder || sha3(_newFounderAddr, founderHash, _oneTimesharedPhrase) != tempHashes[_newFounderAddr]) throw;

        previousFounders.push(founder);
        founder = _newFounderAddr;
        FounderSwitchedEvent(_newFounderAddr);

        return true;
    }

    /**
    * @notice Adding `_backer.address()` as an early backer
    * @dev Add Early backers to Contract setting the transacton details
    * @param _backer The address of the Superdao backer
    * @param _tokenPrice The price/rate at which the Superdao tokens were bought
    * @param _tokenAmount The total number of Superdao token purcgased at the indicated rate
    * @param _privatePhrase Secret or passphrase
    * @param _backerRank Rank of the backer in the backers list
    * @return Thre index of _backer  in the backers list
    */
    function setPrepaid(address _backer, uint _tokenPrice, uint _tokenAmount, string _privatePhrase, uint _backerRank)
        external
        founderCall
        returns (uint)
    {
        if (_tokenPrice == 0 || _tokenAmount == 0 || claimedPrepaidUnits>0 ||
            _tokenAmount + prepaidUnits + claimedUnits > promissoryUnits) throw;
        if (earlyBackerList.length == numOfBackers && backers[_backer].length == 0) throw ;
        if (backers[_backer].length == 0) {
            earlyBackerList.push(_backer);
            backersAddresses.push(_backer);
        }
        backers[_backer].push(backerData(_tokenPrice, _tokenAmount, sha3(_privatePhrase, _backer), true, false, _backerRank));

        prepaidUnits +=_tokenAmount;
        lastPrice = _tokenPrice;

        AddedPrepaidTokensEvent(_backer, backers[_backer].length - 1, _tokenPrice, _tokenAmount);

        return backers[_backer].length - 1;
    }

    /**
    * @notice Claiming `_tokenAmount.number()` Superdao tokens by `msg.sender.address()`
    * @dev Claim Superdao Early backer tokens
    * @param _index index of tokens to claim
    * @param _boughtTokensPrice Price at which the Superdao tokens were bought
    * @param _tokenAmount Number of Superdao tokens to be claimed
    * @param _privatePhrase Secret key for Offline shared pre-hashed phrase offline price negotiation for online attestation of uperdao tokens ownership
    * @param _backerRank Backer rank of the backer in the Superdao
    */
    function claimPrepaid(uint _index, uint _boughtTokensPrice, uint _tokenAmount, string _privatePhrase, uint _backerRank)
        external
        EarliestBackersSet
    {
        if(backers[msg.sender][_index].prepaid == true &&
           backers[msg.sender][_index].claimed == false &&
           backers[msg.sender][_index].tokenAmount == _tokenAmount &&
           backers[msg.sender][_index].tokenPrice == _boughtTokensPrice &&
           backers[msg.sender][_index].privateHash == sha3( _privatePhrase, msg.sender) &&
           backers[msg.sender][_index].backerRank == _backerRank)
        {
            backers[msg.sender][_index].claimed = true;
            claimedPrepaidUnits += _tokenAmount;

            PrepaidTokensClaimedEvent(msg.sender, _index, _boughtTokensPrice, _tokenAmount);
        } else {
            throw;
        }
    }

    /**
    * @notice `msg.sender.address()` is Purchasing `(msg.value / lastPrice).toFixed(0)` Superdao Tokens at `lastPrice`
    * @dev Purchase new Superdao Tokens if the amount of tokens are still available for purchase
    */
    function claim()
        payable
        external
        MinimumBackersClaimed
   {
        if (lastPrice == 0) throw;

        //Effective discount for Pre-crowdfunding backers of 40% Leaving effective rate of 60%
        uint discountPrice = lastPrice * discountAmount / divisor;

        uint tokenAmount = (msg.value / discountPrice);//Effect the discount rate 0f 40%

        if (tokenAmount + claimedUnits + prepaidUnits > promissoryUnits) throw;

        if (backers[msg.sender].length == 0) {
            backersAddresses.push(msg.sender);
        }
        backers[msg.sender].push(backerData(discountPrice, tokenAmount, sha3(msg.sender), false, true, 0));

        claimedUnits += tokenAmount;

        TokensClaimedEvent(msg.sender, backers[msg.sender].length - 1, discountPrice, tokenAmount);
    }

    /**
     * @notice checking `_backerAddress.address()` Superdao Token balance: `index`
     * @dev Check Token balance by index of backer, return values can be used to instantiate a backerData struct
     * @param _backerAddress The Backer's address
     * @param index The balance to check
     * @return tokenPrice The Price at which the tokens were bought
     * @return tokenAmount The number of tokens that were bought
     * @return privateHash Secret phrase for authentication by the backer
     * @return prepaid True if backer is an early backer
     * @return claimed True if the Token has already been claimed by the backer
     */
    function checkBalance(address _backerAddress, uint index) constant returns (uint, uint, bytes32, bool, bool){
        return (
            backers[_backerAddress][index].tokenPrice,
            backers[_backerAddress][index].tokenAmount,
            backers[_backerAddress][index].privateHash,
            backers[_backerAddress][index].prepaid,
            backers[_backerAddress][index].claimed
            );
    }

    /**
    * @notice Approving withdrawal `_withdrawalID`
    * @dev Approve a withdrawal from the Superdao and mark the withdrawal as spent
    * @param _withdrawalID The ID of the withdrawal
    */
    function approveWithdraw(uint _withdrawalID)
        external
        backerCheck(_withdrawalID)
    {
        withdrawalsVotes[msg.sender][_withdrawalID] = true;

        uint backerStake = 0;
        for (uint i = 0; i < backers[msg.sender].length; i++) {
            backerStake += backers[msg.sender][i].tokenAmount;
        }
        withdrawals[_withdrawalID].backerApprovals.push(msg.sender);
        withdrawals[_withdrawalID].totalStake += backerStake;

        WithdrawalVotedEvent(_withdrawalID, msg.sender, backerStake, withdrawals[_withdrawalID].totalStake);

        if(withdrawals[_withdrawalID].totalStake >= (claimedPrepaidUnits + claimedUnits) / 3) {
            uint amountPerAddr;
            bool isMultiPayment = withdrawals[_withdrawalID].destination.length > 1;

            if(isMultiPayment == false){
                amountPerAddr = withdrawals[_withdrawalID].Amount;
            }
            else {
                amountPerAddr = withdrawals[_withdrawalID].Amount / withdrawals[_withdrawalID].destination.length;
            }

            withdrawals[_withdrawalID].approved = true;
            withdrawals[_withdrawalID].spent = true;

            for(i = 0; i < withdrawals[_withdrawalID].destination.length; i++){
                if(!withdrawals[_withdrawalID].destination[i].send(amountPerAddr)) throw;
            }

            WithdrawalApproved(_withdrawalID,
                withdrawals[_withdrawalID].totalStake,
                isMultiPayment,
                withdrawals[_withdrawalID].Amount,
                withdrawals[_withdrawalID].reason);
        }
    }

    /**
    * @notice Requestng withdrawal of `_totalAmount` to `_destination.address()`
    * @dev Create a new withdrawal request
    * @param _totalAmount The total amount of tokens to be withdrawan, should be equal to the total number of owned tokens
    * @param _reason Reason/Description for the withdrawal
    * @param _destination The receiving address
    */
    function withdraw(uint _totalAmount, bytes _reason, address[] _destination)
        external
        founderCall
    {
        if (this.balance < _totalAmount) throw;

        uint withdrawalID = withdrawals.length++;

        withdrawals[withdrawalID].Amount = _totalAmount;
        withdrawals[withdrawalID].reason = _reason;
        withdrawals[withdrawalID].destination = _destination;
        withdrawals[withdrawalID].approved = false;
        withdrawals[withdrawalID].spent = false;

        WithdrawalCreatedEvent(withdrawalID, _totalAmount, _reason);
    }

    /**
    * @notice Backer `_bacherAddr.address()` is redeeming `_amount` Superdao Tokens
    * @dev Check if backer tokens have been claimed but not redeemed, then redeem them
    * @param _amount The total number of redeemable tokens
    * @param _backerAddr The address of the backer
    * @return True if tokens were successfully redeemed else false
    */
    function redeem(uint _amount, address _backerAddr) returns(bool){
        if (backersRedeemed[_backerAddr] == true) {
            return false;
        }

        uint totalTokens = 0;

        for (uint i = 0; i < backers[_backerAddr].length; i++) {
            if (backers[_backerAddr][i].claimed == false) {
                return false;
            }
            totalTokens += backers[_backerAddr][i].tokenAmount;
        }

        if (totalTokens == _amount){
            backersRedeemed[_backerAddr] = true;

            RedeemEvent(_backerAddr, totalTokens);

            return true;
        }
        else {
            return false;
        }
    }

    /**
    * @notice check withdrawal status of `_withdrawalID`
    * @dev Get the withdrawal of a withdrawal. Return values can be used to instantiate a withdrawalData struct
    * @param _withdrawalID The ID of the withdrawal
    * @return Amount The Amount requested in the withdrawal
    * @return approved True if the withdrawal has been approved
    * @return reason Reason/Description of the Withdrawal
    * @return backerApprovals Addresses of backers who approved the withdrawal
    * @return totalStake Total number of tokens which backed the withdrawal(Total number of tokens owned by backers who approved the withdrawal)
    * @return destination Receiving address of the withdrawal
    */
    function getWithdrawalData(uint _withdrawalID) constant public returns (uint, bool, bytes, address[], uint, address[]){
        return (
            withdrawals[_withdrawalID].Amount,
            withdrawals[_withdrawalID].approved,
            withdrawals[_withdrawalID].reason,
            withdrawals[_withdrawalID].backerApprovals,
            withdrawals[_withdrawalID].totalStake,
            withdrawals[_withdrawalID].destination);
    }

    modifier founderCall{
        if (msg.sender != founder) throw;
        _;
    }

    modifier backerCheck(uint _withdrawalID){
        if(backers[msg.sender].length == 0 || withdrawals[_withdrawalID].spent == true || withdrawalsVotes[msg.sender][_withdrawalID] == true) throw;
        _;
    }

    modifier EarliestBackersSet{
       if(earlyBackerList.length < numOfBackers) throw;
       _;
    }

    modifier MinimumBackersClaimed(){
      if(prepaidUnits == 0 ||
        claimedPrepaidUnits == 0 ||
        (claimedPrepaidUnits * divisor / prepaidUnits) < minimumPrepaidClaimedPercent) {
            throw;
        }
      _;
    }

    /*
     * Safeguard function.
     * This function gets executed if a transaction with invalid data is sent to
     * the contract or just ether without data.
     */
    function () {
        throw;
    }

}

pragma solidity ^0.4.8;

//This contract is backed by the constitution of SuperDAO deployed at : .
//The constitution of the superdao is the social contract, terms, founding principles and definitions of the vision,
//mission, anti-missions, rules and operation guidelines of Superdao.
//The total number of 3,000,000 represents 3% of 100,000,000 immutable number of superdao tokens,
//which is the alloted budget of operation for the earliest funding activities.
//Every prommissory token is exchangeable for the real SuperDAO tokens on a one on one basis.
//Promissiory contract will be deployed with the actual superdao token contract.
//Early backers can call the "redeem" function on the actual token contract to exchange promissory tokens for the final tokens.

/**
 * @title Promisory Token Contract
 * @author ola
 * --- Collaborators ---
 * @author zlatinov
 * @author panos
 * @author yemi
 * @author archil
 * @author anthony
 */
contract PromissoryToken {

    event FounderSwitchRequestEvent(address _newFounderAddr);
    event FounderSwitchedEvent(address _newFounderAddr);
    event CofounderSwitchedEvent(address _newCofounderAddr);

    event AddedPrepaidTokensEvent(address backer, uint index, uint price, uint amount);
    event PrepaidTokensClaimedEvent(address backer, uint index, uint price, uint amount);
    event TokensClaimedEvent(address backer, uint index, uint price, uint amount);

    event RedeemEvent(address backer, uint amount);

    event WithdrawalCreatedEvent(uint withdrawalId, uint amount, bytes reason);
    event WithdrawalVotedEvent(uint withdrawalId, address backer, uint backerStakeWeigth, uint totalStakeWeight);
    event WithdrawalApproved(uint withdrawalId, uint stakeWeight, bool isMultiPayment, uint amount, bytes reason);

    address founder; //deployer of constitution and PromissoryToken
    bytes32 founderHash; // hash must be confirmed in order to replace founder address
    mapping(address => bytes32) tempHashes; // structure to contain new address to hash storage,
    address cofounder;//helper to aid founder key exchange in case of key loss
    address [] public previousFounders; //list of addresses replaced using the switching process.
    uint constant discountAmount = 60; //discount amount
    uint constant divisor = 100; //divisor to get discount value

    uint public constant minimumPrepaidClaimedPercent = 65;
    uint public promissoryUnits = 3000000; //amount of tokens contants set
    uint public prepaidUnits = 0; //prepaid and set by founder out of 3 million tokens
    uint public claimedUnits = 0; //claimed tokens out of 3 million tokens
    uint public claimedPrepaidUnits = 0; //claimed tokens out of the early backer's tokens/prepaidUnits
    uint public redeemedTokens = 0; //number of tokens out of claimed tokens, redeemed by superdao token call
    uint public lastPrice; //latest price of token acquired by backer in Wei
    uint public numOfBackers; //number of early backers

    struct backerData {
       uint tokenPrice;
       uint tokenAmount;
       bytes32 privateHash;
       bool prepaid;
       bool claimed;
       uint backerRank;
    }

    address[] public earlyBackerList; //addresses of earliest backers
    address[] public backersAddresses; //addresses of all backers
    mapping(address => backerData[]) public backers;// backer address to backer info mapping
    mapping(address => bool) public backersRedeemed;

    struct withdrawalData {
       uint Amount;
       bool approved;
       bool spent;
       bytes reason;
       address[] backerApprovals;
       uint totalStake;
       address[] destination;
    }

    withdrawalData[] public withdrawals; // Data structure specifying withdrawal
    mapping(address => mapping(uint => bool)) public withdrawalsVotes;

    /**
    * @notice Deploy PromissoryToken contract with `msg.sender.address()` as founder with `_prepaidBackers.number()` prepaid backers
    * @dev This is the constructor of the promisory token contract
    * @param _founderHash Founders password hash, preferable a message digest to further obfuscate duplicaion
    * @param _cofounderAddress The helper cofounder to aid founder key exchange in case of key loss/
    * @param _numOfBackers The number of Early backers. Will be used to control setting early backers
    */
    function PromissoryToken( bytes32 _founderHash, address _cofounderAddress, uint _numOfBackers){
        founder = msg.sender;
        founderHash = sha3(_founderHash);
        cofounder = _cofounderAddress;
        numOfBackers = _numOfBackers;
    }

    /**
    * @notice `msg.sender.address()` updating cofounder address to `_newFounderAddr.address()`
    * @dev allows cofounder to switch out addres for a new one.Can be repeated as many times as needed
    * @param _newCofounderAddr New Address of Cofounder
    * @return True if the coFounder address successfully updated
    */
    function cofounderSwitchAddress(address _newCofounderAddr) external returns (bool success){
        if (msg.sender != cofounder) throw;

        cofounder = _newCofounderAddr;
        CofounderSwitchedEvent(_newCofounderAddr);

        return true;
    }

    /**
    * @notice Founder address update to `_newFounderAddr.address()` is being requested
    * @dev founderSwitchAddress founder indicates intent to switch addresses with new address,
    * hash of pass phrase and a "onetime shared phrase shared with coufounder"
    * @param _founderHash Secret Key to be used to confirm Address update
    * @param _oneTimesharedPhrase Secret phrase to be shared with coFounder to approve Address update
    * @return True if Address switch request successfully created and Temporary hash Values set
    */
    function founderSwitchRequest(bytes32 _founderHash, bytes32 _oneTimesharedPhrase) returns (bool success){
        if(sha3(_founderHash) != founderHash) throw;

        tempHashes[msg.sender] = sha3(msg.sender, founderHash, _oneTimesharedPhrase);
        FounderSwitchRequestEvent(msg.sender);

        return true;
    }

   /**
    * @notice `msg.sender.address()` approving `_newFounderAddr.address()` as new founder address
    * @dev CofounderSwitchAddress which allows previously set cofounder to approve address
    * switch by founder. Must have a one time shared phrase thats is shared with founder that corresponding with a
    * hashed value.
    * @param _newFounderAddr The address of Founder to be newly set
    * @param _oneTimesharedPhrase Secret Keyword to be provide access to the approval function
    * @return True if new Founder address successfully approved
    */
    function cofounderApproveSwitchRequest(address _newFounderAddr, bytes32 _oneTimesharedPhrase) external returns (bool success){
        if(msg.sender != cofounder || sha3(_newFounderAddr, founderHash, _oneTimesharedPhrase) != tempHashes[_newFounderAddr]) throw;

        previousFounders.push(founder);
        founder = _newFounderAddr;
        FounderSwitchedEvent(_newFounderAddr);

        return true;
    }

    /**
    * @notice Adding `_backer.address()` as an early backer
    * @dev Add Early backers to Contract setting the transacton details
    * @param _backer The address of the Superdao backer
    * @param _tokenPrice The price/rate at which the Superdao tokens were bought
    * @param _tokenAmount The total number of Superdao token purcgased at the indicated rate
    * @param _privatePhrase Secret or passphrase
    * @param _backerRank Rank of the backer in the backers list
    * @return Thre index of _backer  in the backers list
    */
    function setPrepaid(address _backer, uint _tokenPrice, uint _tokenAmount, string _privatePhrase, uint _backerRank)
        external
        founderCall
        returns (uint)
    {
        if (_tokenPrice == 0 || _tokenAmount == 0 || claimedPrepaidUnits>0 ||
            _tokenAmount + prepaidUnits + claimedUnits > promissoryUnits) throw;
        if (earlyBackerList.length == numOfBackers && backers[_backer].length == 0) throw ;
        if (backers[_backer].length == 0) {
            earlyBackerList.push(_backer);
            backersAddresses.push(_backer);
        }
        backers[_backer].push(backerData(_tokenPrice, _tokenAmount, sha3(_privatePhrase, _backer), true, false, _backerRank));

        prepaidUnits +=_tokenAmount;
        lastPrice = _tokenPrice;

        AddedPrepaidTokensEvent(_backer, backers[_backer].length - 1, _tokenPrice, _tokenAmount);

        return backers[_backer].length - 1;
    }

    /**
    * @notice Claiming `_tokenAmount.number()` Superdao tokens by `msg.sender.address()`
    * @dev Claim Superdao Early backer tokens
    * @param _index index of tokens to claim
    * @param _boughtTokensPrice Price at which the Superdao tokens were bought
    * @param _tokenAmount Number of Superdao tokens to be claimed
    * @param _privatePhrase Secret key for Offline shared pre-hashed phrase offline price negotiation for online attestation of uperdao tokens ownership
    * @param _backerRank Backer rank of the backer in the Superdao
    */
    function claimPrepaid(uint _index, uint _boughtTokensPrice, uint _tokenAmount, string _privatePhrase, uint _backerRank)
        external
        EarliestBackersSet
    {
        if(backers[msg.sender][_index].prepaid == true &&
           backers[msg.sender][_index].claimed == false &&
           backers[msg.sender][_index].tokenAmount == _tokenAmount &&
           backers[msg.sender][_index].tokenPrice == _boughtTokensPrice &&
           backers[msg.sender][_index].privateHash == sha3( _privatePhrase, msg.sender) &&
           backers[msg.sender][_index].backerRank == _backerRank)
        {
            backers[msg.sender][_index].claimed = true;
            claimedPrepaidUnits += _tokenAmount;

            PrepaidTokensClaimedEvent(msg.sender, _index, _boughtTokensPrice, _tokenAmount);
        } else {
            throw;
        }
    }

    /**
    * @notice `msg.sender.address()` is Purchasing `(msg.value / lastPrice).toFixed(0)` Superdao Tokens at `lastPrice`
    * @dev Purchase new Superdao Tokens if the amount of tokens are still available for purchase
    */
    function claim()
        payable
        external
        MinimumBackersClaimed
   {
        if (lastPrice == 0) throw;

        //Effective discount for Pre-crowdfunding backers of 40% Leaving effective rate of 60%
        uint discountPrice = lastPrice * discountAmount / divisor;

        uint tokenAmount = (msg.value / discountPrice);//Effect the discount rate 0f 40%

        if (tokenAmount + claimedUnits + prepaidUnits > promissoryUnits) throw;

        if (backers[msg.sender].length == 0) {
            backersAddresses.push(msg.sender);
        }
        backers[msg.sender].push(backerData(discountPrice, tokenAmount, sha3(msg.sender), false, true, 0));

        claimedUnits += tokenAmount;

        TokensClaimedEvent(msg.sender, backers[msg.sender].length - 1, discountPrice, tokenAmount);
    }

    /**
     * @notice checking `_backerAddress.address()` Superdao Token balance: `index`
     * @dev Check Token balance by index of backer, return values can be used to instantiate a backerData struct
     * @param _backerAddress The Backer's address
     * @param index The balance to check
     * @return tokenPrice The Price at which the tokens were bought
     * @return tokenAmount The number of tokens that were bought
     * @return privateHash Secret phrase for authentication by the backer
     * @return prepaid True if backer is an early backer
     * @return claimed True if the Token has already been claimed by the backer
     */
    function checkBalance(address _backerAddress, uint index) constant returns (uint, uint, bytes32, bool, bool){
        return (
            backers[_backerAddress][index].tokenPrice,
            backers[_backerAddress][index].tokenAmount,
            backers[_backerAddress][index].privateHash,
            backers[_backerAddress][index].prepaid,
            backers[_backerAddress][index].claimed
            );
    }

    /**
    * @notice Approving withdrawal `_withdrawalID`
    * @dev Approve a withdrawal from the Superdao and mark the withdrawal as spent
    * @param _withdrawalID The ID of the withdrawal
    */
    function approveWithdraw(uint _withdrawalID)
        external
        backerCheck(_withdrawalID)
    {
        withdrawalsVotes[msg.sender][_withdrawalID] = true;

        uint backerStake = 0;
        for (uint i = 0; i < backers[msg.sender].length; i++) {
            backerStake += backers[msg.sender][i].tokenAmount;
        }
        withdrawals[_withdrawalID].backerApprovals.push(msg.sender);
        withdrawals[_withdrawalID].totalStake += backerStake;

        WithdrawalVotedEvent(_withdrawalID, msg.sender, backerStake, withdrawals[_withdrawalID].totalStake);

        if(withdrawals[_withdrawalID].totalStake >= (claimedPrepaidUnits + claimedUnits) / 3) {
            uint amountPerAddr;
            bool isMultiPayment = withdrawals[_withdrawalID].destination.length > 1;

            if(isMultiPayment == false){
                amountPerAddr = withdrawals[_withdrawalID].Amount;
            }
            else {
                amountPerAddr = withdrawals[_withdrawalID].Amount / withdrawals[_withdrawalID].destination.length;
            }

            withdrawals[_withdrawalID].approved = true;
            withdrawals[_withdrawalID].spent = true;

            for(i = 0; i < withdrawals[_withdrawalID].destination.length; i++){
                if(!withdrawals[_withdrawalID].destination[i].send(amountPerAddr)) throw;
            }

            WithdrawalApproved(_withdrawalID,
                withdrawals[_withdrawalID].totalStake,
                isMultiPayment,
                withdrawals[_withdrawalID].Amount,
                withdrawals[_withdrawalID].reason);
        }
    }

    /**
    * @notice Requestng withdrawal of `_totalAmount` to `_destination.address()`
    * @dev Create a new withdrawal request
    * @param _totalAmount The total amount of tokens to be withdrawan, should be equal to the total number of owned tokens
    * @param _reason Reason/Description for the withdrawal
    * @param _destination The receiving address
    */
    function withdraw(uint _totalAmount, bytes _reason, address[] _destination)
        external
        founderCall
    {
        if (this.balance < _totalAmount) throw;

        uint withdrawalID = withdrawals.length++;

        withdrawals[withdrawalID].Amount = _totalAmount;
        withdrawals[withdrawalID].reason = _reason;
        withdrawals[withdrawalID].destination = _destination;
        withdrawals[withdrawalID].approved = false;
        withdrawals[withdrawalID].spent = false;

        WithdrawalCreatedEvent(withdrawalID, _totalAmount, _reason);
    }

    /**
    * @notice Backer `_bacherAddr.address()` is redeeming `_amount` Superdao Tokens
    * @dev Check if backer tokens have been claimed but not redeemed, then redeem them
    * @param _amount The total number of redeemable tokens
    * @param _backerAddr The address of the backer
    * @return True if tokens were successfully redeemed else false
    */
    function redeem(uint _amount, address _backerAddr) returns(bool){
        if (backersRedeemed[_backerAddr] == true) {
            return false;
        }

        uint totalTokens = 0;

        for (uint i = 0; i < backers[_backerAddr].length; i++) {
            if (backers[_backerAddr][i].claimed == false) {
                return false;
            }
            totalTokens += backers[_backerAddr][i].tokenAmount;
        }

        if (totalTokens == _amount){
            backersRedeemed[_backerAddr] = true;

            RedeemEvent(_backerAddr, totalTokens);

            return true;
        }
        else {
            return false;
        }
    }

    /**
    * @notice check withdrawal status of `_withdrawalID`
    * @dev Get the withdrawal of a withdrawal. Return values can be used to instantiate a withdrawalData struct
    * @param _withdrawalID The ID of the withdrawal
    * @return Amount The Amount requested in the withdrawal
    * @return approved True if the withdrawal has been approved
    * @return reason Reason/Description of the Withdrawal
    * @return backerApprovals Addresses of backers who approved the withdrawal
    * @return totalStake Total number of tokens which backed the withdrawal(Total number of tokens owned by backers who approved the withdrawal)
    * @return destination Receiving address of the withdrawal
    */
    function getWithdrawalData(uint _withdrawalID) constant public returns (uint, bool, bytes, address[], uint, address[]){
        return (
            withdrawals[_withdrawalID].Amount,
            withdrawals[_withdrawalID].approved,
            withdrawals[_withdrawalID].reason,
            withdrawals[_withdrawalID].backerApprovals,
            withdrawals[_withdrawalID].totalStake,
            withdrawals[_withdrawalID].destination);
    }

    modifier founderCall{
        if (msg.sender != founder) throw;
        _;
    }

    modifier backerCheck(uint _withdrawalID){
        if(backers[msg.sender].length == 0 || withdrawals[_withdrawalID].spent == true || withdrawalsVotes[msg.sender][_withdrawalID] == true) throw;
        _;
    }

    modifier EarliestBackersSet{
       if(earlyBackerList.length < numOfBackers) throw;
       _;
    }

    modifier MinimumBackersClaimed(){
      if(prepaidUnits == 0 ||
        claimedPrepaidUnits == 0 ||
        (claimedPrepaidUnits * 100 / prepaidUnits) < minimumPrepaidClaimedPercent) {
            throw;
        }
      _;
    }

    /*
     * Safeguard function.
     * This function gets executed if a transaction with invalid data is sent to
     * the contract or just ether without data.
     */
    function () {
        throw;
    }

}