Insert non-zero amounts

Insert non-zero amounts

pragma solidity ^0.4.10;

contract Token {
    uint256 public totalSupply;

    /* This creates an array with all balances */
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    /* This generates a public event on the blockchain that will notify clients */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /* Send tokens */
    function transfer(address _to, uint256 _value) {
        require(balanceOf[msg.sender] >= _value);            // Check if the sender has enough
        require(balanceOf[_to] + _value >= balanceOf[_to]);  // Check for overflows
        balanceOf[msg.sender] -= _value;                     // Subtract from the sender
        balanceOf[_to] += _value;                            // Add the same to the recipient
        Transfer(msg.sender, _to, _value);                   // Notify anyone listening that this transfer took place
    }

    /* Allow another contract to spend some tokens in your behalf */
    function approve(address _spender, uint256 _value) returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    /* A contract attempts to get the tokens */
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        require(balanceOf[_from] >= _value);                 // Check if the sender has enough
        require(balanceOf[_to] + _value >= balanceOf[_to]);  // Check for overflows
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        balanceOf[_from] -= _value;                          // Subtract from the sender
        balanceOf[_to] += _value;                            // Add the same to the recipient
        allowance[_from][msg.sender] -= _value;
        Transfer(_from, _to, _value);
        return true;
    }

    uint public id; /* To ensure distinct contracts for different tokens owned by the same owner */
    address public owner;

    function Token(uint _id) {
        owner = msg.sender;
        id = _id;
    }

    /* Allows the owner to mint more tokens */
    function mint(address _to, uint256 _value) returns (bool) {
        require(msg.sender == owner);                        // Only the owner is allowed to mint
        require(balanceOf[_to] + _value >= balanceOf[_to]);  // Check for overflows
        balanceOf[_to] += _value;
        totalSupply += _value;
        return true;
    }
}

// Withdraw contracts with 1 token giving entitlement to 1 wei
contract ExcessWithdraw {
    Token public token;
    uint public release_time;

    function ExcessWithdraw(uint _release_time, Token _token) {
        release_time = _release_time;
        token = _token;
    }

    function () payable {}

    function withdraw() {
        require(now >= release_time);
        require(token.balanceOf(msg.sender) > 0);
        uint amount = token.balanceOf(msg.sender);
        if (!token.transferFrom(msg.sender, this, amount) || !msg.sender.send(amount)) {
            throw;
        }
    }
}

contract TokenDistribution {
    address public owner;
    uint256 public target_in_wei;                                 /* Minimum amount to collect - otherwise return everything */
    uint256 public cap_in_wei;                                    /* Maximum amount to accept - return the rest */
    uint256 public tokens_to_mint;                                /* How many tokens need to be issued */
    uint256 constant INITIAL_DURATION = 1 weeks;
    uint256 constant TIME_EXTENSION_FROM_DOUBLING = 2 days;
    uint256 constant TIME_OF_HALF_DECAY = 6 hours;
    uint256 constant MAX_LOCK_WEEKS = 100;                        /* Maximum number of weeks that the excess contribution can be locked for */
    uint256 constant FIXED_POINT_ONE = 1000000000000;             /* Equivalent of number "1" for fixed point arithmetics */
    uint256 constant FIXED_POINT_PRC = 1070000000000;             /* Equivalent of number "1.07" for fixed point arithmetics */
    Token public token;                                           /* Token contract where sold tokens are minted */
    uint256 public end_time;                                      /* Current end time */
    uint256 last_time = 0;                                        /* Timestamp of the latest contribution */
    uint256 public ema = 0;                                       /* Current value of the Exponential Moving Average */
    uint256 public ema_divisor = 0;                               /* Amount of wei given, including what has escaped */
    uint256 public total_wei_given = 0;                           /* Total amount of wei given via contribute function, minus escapes */
    uint256 public total_wei_accepted = 0;                        /* Total amount of wei accepted */
    mapping (uint256 => Token) public excess_tokens;              /* Excess tokens organised by lock weeks */
    mapping (uint256 => ExcessWithdraw) public excess_withdraws;  /* Excess withdraw contracts organised by lock weeks */
    mapping (uint256 => uint256) public wei_given_to_bucket;      /* Amount of wei given to specific bucket (lock_weeks is key in the mapping) */
    mapping (uint256 => uint256) public wei_accepted_from_bucket; /* Amount of wei accepted from specific bucket (lock_weeks is the key in the mapping) */
    mapping (address => mapping (uint256 => uint256)) public contributions; /* Contributions of a participant (first key) to a bucket (second key) */
    uint256 public last_bucket_closed = MAX_LOCK_WEEKS + 1;       /* Counter (goes from max_lock_weeks to 0) used to finalise bucket by bucket */
    uint256 public cap_remainder;                                 /* As the buckets are getting closed, the cap_remainder reduced to what is left to allocate */

    // sqrt(2), sqrt(sqrt(2)), sqrt(sqrt(sqrt(2))), ...
    uint256[] FIXED_POINT_DECAYS =
        [1414213562370, 1189207115000, 1090507732670, 1044273782430, 1021897148650, 1010889286050, 1005429901110, 1002711275050, 1002711275050, 1000677130690,
         1000338508050, 1000169239710, 1000084616270, 1000042307240, 1000021153400, 1000010576640, 1000005288310, 1000002644150, 1000001322070, 1000000661040];

    function TokenDistribution(uint256 _target_in_wei, uint256 _cap_in_wei, uint256 _tokens_to_mint) {
        owner = msg.sender;
        target_in_wei = _target_in_wei;
        cap_in_wei = _cap_in_wei;
        cap_remainder = _cap_in_wei;
        tokens_to_mint = _tokens_to_mint;
        token = new Token(MAX_LOCK_WEEKS + 1);
        end_time = now + INITIAL_DURATION;
    }

    function exponential_decay(uint256 value, uint256 time) private returns (uint256 decayed) {
        if (time == 0) {
            return value;
        }
        // First, we halve the value for each unit of TIME_OF_HALF_DECAY
        uint256 shifts = time / TIME_OF_HALF_DECAY;
        if (shifts >= 256) {
            // Since uint is 256 bit, shifting more than 256 would produce zero
            return 0;
        }
        uint256 v = value >> shifts;
        uint256 t = time % TIME_OF_HALF_DECAY;
        uint256 decay = TIME_OF_HALF_DECAY; // This is half of the time of half decay
        for(uint8 i = 0; (i<20) && (decay > 0) && (v > 0); ++i) {
            decay >>= 1;
            if (t >= decay) {
                v = v * FIXED_POINT_ONE / FIXED_POINT_DECAYS[i];
                t -= decay;
            }
        }
        return v;
    }

    function contribute(uint256 lock_weeks) payable {
        require(now <= end_time);   // Check that the sale has not ended
        require(msg.value > 0);     // Check that something has been sent
        require(lock_weeks <= MAX_LOCK_WEEKS);
        contributions[msg.sender][lock_weeks] += msg.value;
        wei_given_to_bucket[lock_weeks] += msg.value;
        total_wei_given += msg.value;

        // Do not apply extension of the end_time if we are refilling the gap left by escapes
        if (total_wei_given <= ema_divisor) return;

        // Time weighted exponential moving average is computed over the size of the contributions
        ema = msg.value + exponential_decay(ema, now - last_time);
        last_time = now;
        ema_divisor = total_wei_given;
        uint256 extension = ema * TIME_EXTENSION_FROM_DOUBLING / ema_divisor;
        if (extension > TIME_EXTENSION_FROM_DOUBLING) {
            extension = TIME_EXTENSION_FROM_DOUBLING;
        }
        uint256 extended_time = now + extension;
        if (extended_time > end_time) {
            end_time = extended_time;
        }
    }

    function escape(uint256 bucket) {
        require(last_bucket_closed > MAX_LOCK_WEEKS);   // Check that no buckets are yet closed
        uint256 contribution = contributions[msg.sender][bucket];
        require(contribution > 0);
        contributions[msg.sender][bucket] = 0;
        wei_given_to_bucket[bucket] -= contribution;
        total_wei_given -= contribution;
        require(msg.sender.send(contribution));
    }

    function close_next_bucket() {
        require(now > end_time);                   /* Can only close buckets after the end of sale */
        require(last_bucket_closed > 0);           /* Not all buckets closed yet */
        require(total_wei_given >= target_in_wei); /* Target must be reached */
        uint256 bucket = last_bucket_closed - 1;
        while (bucket > 0 && wei_given_to_bucket[bucket] == 0) {
            bucket--;
        }
        uint256 bucket_contribution = wei_given_to_bucket[bucket];
        if (bucket_contribution > 0) {
            // Current bucket will get the biggest contritubion multiplier (due to highest lock time)
            // The muliplier decays by 1.07 as the lock time decreased by a week
            uint256 contribution_multiplier = FIXED_POINT_ONE;
            uint256 contribution_sum = bucket_contribution;
            uint256 b = bucket;
            while (b > 0) {
                b--;
                contribution_multiplier = contribution_multiplier * FIXED_POINT_ONE / FIXED_POINT_PRC;
                contribution_sum += wei_given_to_bucket[b] * contribution_multiplier / FIXED_POINT_ONE;
            }
            // Compute accepted contribution for this bucket
            uint256 accepted = cap_remainder * wei_given_to_bucket[bucket] / contribution_sum;
            if (accepted > bucket_contribution) {
                accepted = bucket_contribution;
            }
            wei_accepted_from_bucket[bucket] = accepted;
            total_wei_accepted += accepted;
            cap_remainder -= accepted;
            if (accepted < bucket_contribution) {
                // Only call if there is an excess
                move_excess_for_bucket(bucket, bucket_contribution - accepted);
            }
        }
        last_bucket_closed = bucket;
    }

    function move_excess_for_bucket(uint256 bucket, uint256 excess) private {
        Token token_contract = new Token(bucket);
        excess_tokens[bucket] = token_contract;
        ExcessWithdraw withdraw_contract = new ExcessWithdraw(end_time + bucket * (1 weeks), token_contract);
        excess_withdraws[bucket] = withdraw_contract;
        require(withdraw_contract.send(excess));
    }

    // Claim tokens for players and send ether to the owner
    function claim_tokens(address player, uint256 bucket) {
        require(last_bucket_closed == 0); /* Claims only allowed when all buckets are closed */
        uint256 contribution = contributions[player][bucket];
        require(contribution > 0);
        contributions[player][bucket] = 0;
        uint256 wei_accepted = contribution * wei_accepted_from_bucket[bucket] / wei_given_to_bucket[bucket];
        uint256 tokens = wei_accepted * tokens_to_mint / total_wei_accepted;
        require(tokens == 0 || token.mint(player, tokens));
        Token excess_token = excess_tokens[bucket];
        uint256 excess = contribution - wei_accepted;
        require(excess == 0 || excess_token.mint(player, excess));
        require(wei_accepted == 0 || owner.send(wei_accepted));
    }
}

contract PrizePot {
    TokenDistribution public dist;

    function PrizePot(TokenDistribution _dist) {
        dist = _dist;
    }

    function() payable {}

    function claim_prize() {
        Token token = dist.token();
        uint256 token_amount = token.balanceOf(msg.sender);
        uint256 wei_amount = this.balance * token_amount / (dist.tokens_to_mint() - token.balanceOf(this));
        require(token_amount == 0 || token.transferFrom(msg.sender, this, token_amount));
        require(wei_amount == 0 || msg.sender.send(wei_amount));
    }

    function cancel() {
        require(now > dist.end_time());
        require(dist.total_wei_given() < dist.target_in_wei());
        require(dist.owner().send(this.balance));
    }
}

contract TokenGame is TokenDistribution {

    PrizePot public prize_pot;

    function TokenGame() TokenDistribution(1000000 /* target */, 1000000000 /* cap */, 1000000000 /* tokens to mint */) {
        prize_pot = new PrizePot(this);
    }
}

pragma solidity ^0.4.10;

contract Token {
    uint256 public totalSupply;

    /* This creates an array with all balances */
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    /* This generates a public event on the blockchain that will notify clients */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /* Send tokens */
    function transfer(address _to, uint256 _value) {
        require(balanceOf[msg.sender] >= _value);            // Check if the sender has enough
        require(balanceOf[_to] + _value >= balanceOf[_to]);  // Check for overflows
        balanceOf[msg.sender] -= _value;                     // Subtract from the sender
        balanceOf[_to] += _value;                            // Add the same to the recipient
        Transfer(msg.sender, _to, _value);                   // Notify anyone listening that this transfer took place
    }

    /* Allow another contract to spend some tokens in your behalf */
    function approve(address _spender, uint256 _value) returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    /* A contract attempts to get the tokens */
    function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
        require(balanceOf[_from] >= _value);                 // Check if the sender has enough
        require(balanceOf[_to] + _value >= balanceOf[_to]);  // Check for overflows
        require(_value <= allowance[_from][msg.sender]);     // Check allowance
        balanceOf[_from] -= _value;                          // Subtract from the sender
        balanceOf[_to] += _value;                            // Add the same to the recipient
        allowance[_from][msg.sender] -= _value;
        Transfer(_from, _to, _value);
        return true;
    }

    uint public id; /* To ensure distinct contracts for different tokens owned by the same owner */
    address public owner;

    function Token(uint _id) {
        owner = msg.sender;
        id = _id;
    }

    /* Allows the owner to mint more tokens */
    function mint(address _to, uint256 _value) returns (bool) {
        require(msg.sender == owner);                        // Only the owner is allowed to mint
        require(balanceOf[_to] + _value >= balanceOf[_to]);  // Check for overflows
        balanceOf[_to] += _value;
        totalSupply += _value;
        return true;
    }
}

// Withdraw contracts with 1 token giving entitlement to 1 wei
contract ExcessWithdraw {
    Token public token;
    uint public release_time;

    function ExcessWithdraw(uint _release_time, Token _token) {
        release_time = _release_time;
        token = _token;
    }

    function () payable {}

    function withdraw() {
        require(now >= release_time);
        require(token.balanceOf(msg.sender) > 0);
        uint amount = token.balanceOf(msg.sender);
        if (!token.transferFrom(msg.sender, this, amount) || !msg.sender.send(amount)) {
            throw;
        }
    }
}

contract TokenDistribution {
    address public owner;
    uint256 public target_in_wei;                                 /* Minimum amount to collect - otherwise return everything */
    uint256 public cap_in_wei;                                    /* Maximum amount to accept - return the rest */
    uint256 public tokens_to_mint;                                /* How many tokens need to be issued */
    uint256 constant INITIAL_DURATION = 1 weeks;
    uint256 constant TIME_EXTENSION_FROM_DOUBLING = 2 days;
    uint256 constant TIME_OF_HALF_DECAY = 6 hours;
    uint256 constant MAX_LOCK_WEEKS = 100;                        /* Maximum number of weeks that the excess contribution can be locked for */
    uint256 constant FIXED_POINT_ONE = 1000000000000;             /* Equivalent of number "1" for fixed point arithmetics */
    uint256 constant FIXED_POINT_PRC = 1070000000000;             /* Equivalent of number "1.07" for fixed point arithmetics */
    Token public token;                                           /* Token contract where sold tokens are minted */
    uint256 public end_time;                                      /* Current end time */
    uint256 last_time = 0;                                        /* Timestamp of the latest contribution */
    uint256 public ema = 0;                                       /* Current value of the Exponential Moving Average */
    uint256 public ema_divisor = 0;                               /* Amount of wei given, including what has escaped */
    uint256 public total_wei_given = 0;                           /* Total amount of wei given via contribute function, minus escapes */
    uint256 public total_wei_accepted = 0;                        /* Total amount of wei accepted */
    mapping (uint256 => Token) public excess_tokens;              /* Excess tokens organised by lock weeks */
    mapping (uint256 => ExcessWithdraw) public excess_withdraws;  /* Excess withdraw contracts organised by lock weeks */
    mapping (uint256 => uint256) public wei_given_to_bucket;      /* Amount of wei given to specific bucket (lock_weeks is key in the mapping) */
    mapping (uint256 => uint256) public wei_accepted_from_bucket; /* Amount of wei accepted from specific bucket (lock_weeks is the key in the mapping) */
    mapping (address => mapping (uint256 => uint256)) public contributions; /* Contributions of a participant (first key) to a bucket (second key) */
    uint256 public last_bucket_closed = MAX_LOCK_WEEKS + 1;       /* Counter (goes from max_lock_weeks to 0) used to finalise bucket by bucket */
    uint256 public cap_remainder;                                 /* As the buckets are getting closed, the cap_remainder reduced to what is left to allocate */

    // sqrt(2), sqrt(sqrt(2)), sqrt(sqrt(sqrt(2))), ...
    uint256[] FIXED_POINT_DECAYS =
        [1414213562370, 1189207115000, 1090507732670, 1044273782430, 1021897148650, 1010889286050, 1005429901110, 1002711275050, 1002711275050, 1000677130690,
         1000338508050, 1000169239710, 1000084616270, 1000042307240, 1000021153400, 1000010576640, 1000005288310, 1000002644150, 1000001322070, 1000000661040];

    function TokenDistribution(uint256 _target_in_wei, uint256 _cap_in_wei, uint256 _tokens_to_mint) {
        owner = msg.sender;
        target_in_wei = _target_in_wei;
        cap_in_wei = _cap_in_wei;
        cap_remainder = _cap_in_wei;
        tokens_to_mint = _tokens_to_mint;
        token = new Token(MAX_LOCK_WEEKS + 1);
        end_time = now + INITIAL_DURATION;
    }

    function exponential_decay(uint256 value, uint256 time) private returns (uint256 decayed) {
        if (time == 0) {
            return value;
        }
        // First, we halve the value for each unit of TIME_OF_HALF_DECAY
        uint256 shifts = time / TIME_OF_HALF_DECAY;
        if (shifts >= 256) {
            // Since uint is 256 bit, shifting more than 256 would produce zero
            return 0;
        }
        uint256 v = value >> shifts;
        uint256 t = time % TIME_OF_HALF_DECAY;
        uint256 decay = TIME_OF_HALF_DECAY; // This is half of the time of half decay
        for(uint8 i = 0; (i<20) && (decay > 0) && (v > 0); ++i) {
            decay >>= 1;
            if (t >= decay) {
                v = v * FIXED_POINT_ONE / FIXED_POINT_DECAYS[i];
                t -= decay;
            }
        }
        return v;
    }

    function contribute(uint256 lock_weeks) payable {
        require(now <= end_time);   // Check that the sale has not ended
        require(msg.value > 0);     // Check that something has been sent
        require(lock_weeks <= MAX_LOCK_WEEKS);
        contributions[msg.sender][lock_weeks] += msg.value;
        wei_given_to_bucket[lock_weeks] += msg.value;
        total_wei_given += msg.value;

        // Do not apply extension of the end_time if we are refilling the gap left by escapes
        if (total_wei_given <= ema_divisor) return;

        // Time weighted exponential moving average is computed over the size of the contributions
        ema = msg.value + exponential_decay(ema, now - last_time);
        last_time = now;
        ema_divisor = total_wei_given;
        uint256 extension = ema * TIME_EXTENSION_FROM_DOUBLING / ema_divisor;
        if (extension > TIME_EXTENSION_FROM_DOUBLING) {
            extension = TIME_EXTENSION_FROM_DOUBLING;
        }
        uint256 extended_time = now + extension;
        if (extended_time > end_time) {
            end_time = extended_time;
        }
    }

    function escape(uint256 bucket) {
        require(last_bucket_closed > MAX_LOCK_WEEKS);   // Check that no buckets are yet closed
        uint256 contribution = contributions[msg.sender][bucket];
        require(contribution > 0);
        contributions[msg.sender][bucket] = 0;
        wei_given_to_bucket[bucket] -= contribution;
        total_wei_given -= contribution;
        require(msg.sender.send(contribution));
    }

    function close_next_bucket() {
        require(now > end_time);                   /* Can only close buckets after the end of sale */
        require(last_bucket_closed > 0);           /* Not all buckets closed yet */
        require(total_wei_given >= target_in_wei); /* Target must be reached */
        uint256 bucket = last_bucket_closed - 1;
        while (bucket > 0 && wei_given_to_bucket[bucket] == 0) {
            bucket--;
        }
        uint256 bucket_contribution = wei_given_to_bucket[bucket];
        if (bucket_contribution > 0) {
            // Current bucket will get the biggest contritubion multiplier (due to highest lock time)
            // The muliplier decays by 1.07 as the lock time decreased by a week
            uint256 contribution_multiplier = FIXED_POINT_ONE;
            uint256 contribution_sum = bucket_contribution;
            uint256 b = bucket;
            while (b > 0) {
                b--;
                contribution_multiplier = contribution_multiplier * FIXED_POINT_ONE / FIXED_POINT_PRC;
                contribution_sum += wei_given_to_bucket[b] * contribution_multiplier / FIXED_POINT_ONE;
            }
            // Compute accepted contribution for this bucket
            uint256 accepted = cap_remainder * wei_given_to_bucket[bucket] / contribution_sum;
            if (accepted > bucket_contribution) {
                accepted = bucket_contribution;
            }
            wei_accepted_from_bucket[bucket] = accepted;
            total_wei_accepted += accepted;
            cap_remainder -= accepted;
            if (accepted < bucket_contribution) {
                // Only call if there is an excess
                move_excess_for_bucket(bucket, bucket_contribution - accepted);
            }
        }
        last_bucket_closed = bucket;
    }

    function move_excess_for_bucket(uint256 bucket, uint256 excess) private {
        Token token_contract = new Token(bucket);
        excess_tokens[bucket] = token_contract;
        ExcessWithdraw withdraw_contract = new ExcessWithdraw(end_time + bucket * (1 weeks), token_contract);
        excess_withdraws[bucket] = withdraw_contract;
        require(withdraw_contract.send(excess));
    }

    // Claim tokens for players and send ether to the owner
    function claim_tokens(address player, uint256 bucket) {
        require(last_bucket_closed == 0); /* Claims only allowed when all buckets are closed */
        uint256 contribution = contributions[player][bucket];
        require(contribution > 0);
        contributions[player][bucket] = 0;
        uint256 wei_accepted = contribution * wei_accepted_from_bucket[bucket] / wei_given_to_bucket[bucket];
        uint256 tokens = wei_accepted * tokens_to_mint / total_wei_accepted;
        require(tokens == 0 || token.mint(player, tokens));
        Token excess_token = excess_tokens[bucket];
        uint256 excess = contribution - wei_accepted;
        require(excess == 0 || excess_token.mint(player, excess));
        require(wei_accepted == 0 || owner.send(wei_accepted));
    }
}

contract PrizePot {
    TokenDistribution public dist;

    function PrizePot(TokenDistribution _dist) {
        dist = _dist;
    }

    function() payable {}

    function claim_prize() {
        Token token = dist.token();
        uint256 token_amount = token.balanceOf(msg.sender);
        uint256 wei_amount = this.balance * token_amount / (dist.tokens_to_mint() - token.balanceOf(this));
        require(token_amount == 0 || token.transferFrom(msg.sender, this, token_amount));
        require(wei_amount == 0 || msg.sender.send(wei_amount));
    }

    function cancel() {
        require(now > dist.end_time());
        require(dist.total_wei_given() < dist.target_in_wei());
        require(dist.owner().send(this.balance));
    }
}

contract TokenGame is TokenDistribution {

    PrizePot public prize_pot;

    function TokenGame() TokenDistribution(0, 0, 1000) {
        prize_pot = new PrizePot(this);
    }
}


refactoring, var rename

refactoring, var rename

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;

    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) {
        if (! approvedTraders[msg.sender]) {
            //Only approved fiat trackers can make deposit
            throw;
        }
        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) returns(bool result) {
        if (! approvedTraders[msg.sender]) {
            //Only approved btc trackers can offer bitcoin
            throw;
        }
        //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) approvedPlutusCentral;

    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() {
        approvedPlutusCentral[msg.sender] = true;
        btcTradingVolume = 0;
    }

    function depositFiat(address trader, uint fiatDeposited, CurrencySymbol fiatSymbol, uint btcAsked, bytes20 btcAddress) {
        if (! approvedPlutusCentral[msg.sender]) {
            //Only approved fiat trackers can make deposit
            throw;
        }
        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) returns(bool result) {
        if (! approvedPlutusCentral[msg.sender]) {
            //Only approved btc trackers can offer bitcoin
            throw;
        }
        //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;
    }
}


Update recommendation of JS library for merkle trees

(cherry picked from commit 8d105825e960501a11cf822e8ee03e42ad12821d)

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The proofs can be generated using our JavaScript library
 * https://github.com/OpenZeppelin/merkle-tree[`@openzeppelin/merkle-tree`].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates merkle trees that are safe
 * against this attack out of the box.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0-rc.2) (utils/cryptography/MerkleProof.sol)

pragma solidity ^0.8.0;

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The proofs can be generated using the JavaScript library
 * https://github.com/miguelmota/merkletreejs[merkletreejs].
 * Note: the hashing algorithm should be keccak256 and pair sorting should be enabled.
 *
 * See `test/utils/cryptography/MerkleProof.test.js` for some examples.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the merkle tree could be reinterpreted as a leaf value.
 */
library MerkleProof {
    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Calldata version of {verify}
     *
     * _Available since v4.7._
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf
    ) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leafs & pre-images are assumed to be sorted.
     *
     * _Available since v4.4._
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Calldata version of {processProof}
     *
     * _Available since v4.7._
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = _hashPair(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Calldata version of {multiProofVerify}
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * _Available since v4.7._
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Calldata version of {processMultiProof}.
     *
     * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * _Available since v4.7._
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuild the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 totalHashes = proofFlags.length;

        // Check proof validity.
        require(leavesLen + proof.length - 1 == totalHashes, "MerkleProof: invalid multiproof");

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](totalHashes);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value for the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < totalHashes; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i] ? leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++] : proof[proofPos++];
            hashes[i] = _hashPair(a, b);
        }

        if (totalHashes > 0) {
            return hashes[totalHashes - 1];
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) {
        return a < b ? _efficientHash(a, b) : _efficientHash(b, a);
    }

    function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}


minor comment update

minor comment update

pragma solidity ^0.4.10;
import './BancorEventsDispatcher.sol';
import './TokenChangerInterface.sol';
import './SmartTokenInterface.sol';
import './SafeMath.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

    The Bancor version of the token changer, allows changing between a smart token and other ERC20 tokens and between different ERC20 tokens and themselves

    ERC20 reserve token balance can be virtual, meaning that the calculations are based on the virtual balance instead of relying on
    the actual reserve balance. This is a security mechanism that prevents the need to keep a very large (and valuable) balance in a single contract
*/
contract BancorChanger is BancorEventsDispatcher, TokenChangerInterface, SafeMath {
    struct Reserve {
        uint256 virtualBalance;         // virtual balance
        uint8 ratio;                    // constant reserve ratio (CRR), 1-100
        bool isVirtualBalanceEnabled;   // true if virtual balance is enabled, false if not
        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
    }

    string public version = '0.1';
    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 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) {
        require(_address != 0x0);
        _;
    }

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

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

    // validates reserve ratio range
    modifier validReserveRatio(uint8 _ratio) {
        require(_ratio > 0 && _ratio <= 100);
        _;
    }

    // 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
        _enableVirtualBalance   true to enable virtual balance for the reserve, false to disable it
    */
    function addReserve(address _token, uint8 _ratio, bool _enableVirtualBalance)
        public
        ownerOnly
        inactive
        validAddress(_token)
        validReserveRatio(_ratio)
        returns (bool success)
    {
        require(_token != address(this) && _token != address(token) && !reserves[_token].isSet && totalReserveRatio + _ratio <= 100); // validate input

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

    /*
        updates one of the token reserves
        can only be called by the changer owner

        _reserveToken           address of the reserve token
        _ratio                  constant reserve ratio, 1-100
        _enableVirtualBalance   true to enable virtual balance for the reserve, false to disable it
        _virtualBalance         new reserve's virtual balance
    */
    function updateReserve(address _reserveToken, uint8 _ratio, bool _enableVirtualBalance, uint256 _virtualBalance)
        public
        ownerOnly
        validReserve(_reserveToken)
        validReserveRatio(_ratio)
        returns (bool success)
    {
        Reserve reserve = reserves[_reserveToken];
        require(totalReserveRatio - reserve.ratio + _ratio <= 100); // validate input

        totalReserveRatio = totalReserveRatio - reserve.ratio + _ratio;
        reserve.ratio = _ratio;
        reserve.isVirtualBalanceEnabled = _enableVirtualBalance;
        reserve.virtualBalance = _virtualBalance;
        return true;
    }

    /*
        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;
    }

    /*
        returns the reserve's virtual balance if one is defined, otherwise returns the actual balance

        _reserveToken    reserve token contract address
    */
    function getReserveBalance(address _reserveToken)
        public
        constant
        validReserve(_reserveToken)
        returns (uint256 balance)
    {
        Reserve reserve = reserves[_reserveToken];
        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        return reserve.isVirtualBalanceEnabled ? reserve.virtualBalance : reserveToken.balanceOf(this);
    }

    /*
        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) && _to != address(token) && _amount != 0); // validate input

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        assert(reserveToken.transfer(_to, _amount));

        // update virtual balance if relevant
        Reserve reserve = reserves[_reserveToken];
        if (reserve.isVirtualBalanceEnabled)
            reserve.virtualBalance = safeSub(reserve.virtualBalance, _amount);

        return true;
    }

    /*
        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) && _changer != address(token)); // validate input
        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

        uint256 reserveBalance = getReserveBalance(_reserveToken);
        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

        Reserve reserve = reserves[_reserveToken];
        uint256 reserveBalance = getReserveBalance(_reserveToken);
        assert(reserveBalance != 0); // validate state

        uint256 tokenSupply = token.totalSupply();
        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

        // update virtual balance if relevant
        Reserve reserve = reserves[_reserveToken];
        if (reserve.isVirtualBalanceEnabled)
            reserve.virtualBalance = safeAdd(reserve.virtualBalance, _depositAmount);

        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 = getReserveBalance(_reserveToken);
        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's balance in the smart token
        assert(reserveToken.transfer(msg.sender, amount)); // transfer funds to the caller in the reserve token

        // update virtual balance if relevant
        Reserve reserve = reserves[_reserveToken];
        if (reserve.isVirtualBalanceEnabled)
            reserve.virtualBalance = safeSub(reserve.virtualBalance, amount);

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

        dispatchChange(token, _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';
import './SafeMath.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

    The Bancor version of the token changer, allows changing between a smart token and other ERC20 tokens and between different ERC20 tokens and themselves

    ERC20 reserve token balance can be virtual, meaning that the calculations are based on the virtual balance instead of relying on
    the actual reserve balance. This is a security mechanism that prevents the need to keep a very large (and valuable) balance in a single contract
*/
contract BancorChanger is BancorEventsDispatcher, TokenChangerInterface, SafeMath {
    struct Reserve {
        uint256 virtualBalance;         // virtual balance
        uint8 ratio;                    // constant reserve ratio (CRR), 1-100
        bool isVirtualBalanceEnabled;   // true if virtual balance is enabled, false if not
        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
    }

    string public version = '0.1';
    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) {
        require(_address != 0x0);
        _;
    }

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

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

    // validates reserve ratio range
    modifier validReserveRatio(uint8 _ratio) {
        require(_ratio > 0 && _ratio <= 100);
        _;
    }

    // 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
        _enableVirtualBalance   true to enable virtual balance for the reserve, false to disable it
    */
    function addReserve(address _token, uint8 _ratio, bool _enableVirtualBalance)
        public
        ownerOnly
        inactive
        validAddress(_token)
        validReserveRatio(_ratio)
        returns (bool success)
    {
        require(_token != address(this) && _token != address(token) && !reserves[_token].isSet && totalReserveRatio + _ratio <= 100); // validate input

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

    /*
        updates one of the token reserves
        can only be called by the changer owner

        _reserveToken           address of the reserve token
        _ratio                  constant reserve ratio, 1-100
        _enableVirtualBalance   true to enable virtual balance for the reserve, false to disable it
        _virtualBalance         new reserve's virtual balance
    */
    function updateReserve(address _reserveToken, uint8 _ratio, bool _enableVirtualBalance, uint256 _virtualBalance)
        public
        ownerOnly
        validReserve(_reserveToken)
        validReserveRatio(_ratio)
        returns (bool success)
    {
        Reserve reserve = reserves[_reserveToken];
        require(totalReserveRatio - reserve.ratio + _ratio <= 100); // validate input

        totalReserveRatio = totalReserveRatio - reserve.ratio + _ratio;
        reserve.ratio = _ratio;
        reserve.isVirtualBalanceEnabled = _enableVirtualBalance;
        reserve.virtualBalance = _virtualBalance;
        return true;
    }

    /*
        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;
    }

    /*
        returns the reserve's virtual balance if one is defined, otherwise returns the actual balance

        _reserveToken    reserve token contract address
    */
    function getReserveBalance(address _reserveToken)
        public
        constant
        validReserve(_reserveToken)
        returns (uint256 balance)
    {
        Reserve reserve = reserves[_reserveToken];
        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        return reserve.isVirtualBalanceEnabled ? reserve.virtualBalance : reserveToken.balanceOf(this);
    }

    /*
        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) && _to != address(token) && _amount != 0); // validate input

        ERC20TokenInterface reserveToken = ERC20TokenInterface(_reserveToken);
        assert(reserveToken.transfer(_to, _amount));

        // update virtual balance if relevant
        Reserve reserve = reserves[_reserveToken];
        if (reserve.isVirtualBalanceEnabled)
            reserve.virtualBalance = safeSub(reserve.virtualBalance, _amount);

        return true;
    }

    /*
        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) && _changer != address(token)); // validate input
        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

        uint256 reserveBalance = getReserveBalance(_reserveToken);
        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

        Reserve reserve = reserves[_reserveToken];
        uint256 reserveBalance = getReserveBalance(_reserveToken);
        assert(reserveBalance != 0); // validate state

        uint256 tokenSupply = token.totalSupply();
        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

        // update virtual balance if relevant
        Reserve reserve = reserves[_reserveToken];
        if (reserve.isVirtualBalanceEnabled)
            reserve.virtualBalance = safeAdd(reserve.virtualBalance, _depositAmount);

        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 = getReserveBalance(_reserveToken);
        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's balance in the smart token
        assert(reserveToken.transfer(msg.sender, amount)); // transfer funds to the caller in the reserve token

        // update virtual balance if relevant
        Reserve reserve = reserves[_reserveToken];
        if (reserve.isVirtualBalanceEnabled)
            reserve.virtualBalance = safeSub(reserve.virtualBalance, amount);

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

        dispatchChange(token, _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);
    }
}