increased limit of number of reserve tokens to 65536

pragma solidity ^0.4.9;
import "owned.sol";

/*
    Open issues:
    - throw vs. return value?
    - possibly add modifiers for each stage
    - possibly create a shared standard token contract and inherit from it, both for the BancorToken and for the BancorEtherToken
    - add miner abuse protection
    - allow exchanging between 2 reserve tokens directly? can be done through a 3rd party contract
    - startTrading - looping over the reserve - can run out of gas. Possibly split it and do it as a multi-step process
    - approve - to minimize the risk of the approve/transferFrom attack vector
                (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
                in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value.
                Also relevant to the BancorEtherToken
*/

// interfaces

contract ReserveToken { // any ERC20 standard token
    function balanceOf(address _owner) public constant returns (uint256 balance);
    function transfer(address _to, uint256 _value) public returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
}

contract BancorFormula {
    function calculatePurchaseValue(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _depositAmount) public constant returns (uint256 value);
    function calculateSaleValue(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _sellAmount) public constant returns (uint256 value);
    function newFormula() public constant returns (address newFormula);
}

contract BancorEvents {
    function newToken() public;
    function tokenUpdate() public;
    function tokenTransfer(address _from, address _to, uint256 _value) public;
    function tokenApproval(address _owner, address _spender, uint256 _value) public;
    function tokenConversion(address _reserveToken, address _trader, bool _isPurchase, uint256 _totalSupply,
                             uint256 _reserveBalance, uint256 _tokenAmount, uint256 _reserveAmount) public;
}

/*
    Bancor Token v0.2
*/
contract BancorToken is owned {
    enum Stage { Managed, Crowdsale, Traded }

    string public standard = 'Token 0.1';
    string public name = "";
    string public symbol = "";                          // 1-6 characters
    uint8 public numDecimalUnits = 0;                   // for display purposes only
    uint256 public totalSupply = 0;
    address public formula = 0x0;                       // bancor calculation formula contract address
    address public events = 0x0;                        // bancor events contract address
    address public crowdsale = 0x0;                     // crowdsale contract address
    Stage public stage = Stage.Managed;                 // token stage
    address[] public reserveTokens;                     // ERC20 standard token addresses
    mapping (address => uint8) public reserveRatioOf;   // token addresses -> constant reserve ratio, 1-99
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    // events, can be used to listen to the contract directly, as opposed to through the events contract
    event Update();
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    event Conversion(address indexed _reserveToken, address indexed _trader, bool _isPurchase,
                     uint256 _totalSupply, uint256 _reserveBalance, uint256 _tokenAmount, uint256 _reserveAmount);

    /*
        _name               token name
        _symbol             token short symbol
        _numDecimalUnits    for display purposes only
        _formula            address of a bancor formula contract
        _events             optional, address of a bancor events contract
    */
    function BancorToken(string _name, string _symbol, uint8 _numDecimalUnits, address _formula, address _events) {
        if (bytes(_name).length == 0 || bytes(_symbol).length < 1 || bytes(_symbol).length > 6 || _formula == 0x0) // validate input
            throw;

        name = _name;
        symbol = _symbol;
        numDecimalUnits = _numDecimalUnits;
        formula = _formula;
        events = _events;

        if (events == 0x0)
            return;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.newToken();
    }

    // allows executing a function by the owner in managed stage or by the crowdsale contract in crowdsale stage
    modifier onlyManager {
        if (stage == Stage.Traded ||
            stage == Stage.Managed && msg.sender != owner ||
            stage == Stage.Crowdsale && msg.sender != crowdsale) // validate state & permissions
            throw;
        _;
    }

    /*
        updates the bancor calculation formula contract address
        can only be called by the token owner

        the owner can only update the formula to a new one approved by the current formula's owner

        _formula     new formula contract address
    */
    function setFormula(address _formula) public onlyOwner returns (bool success) {
        BancorFormula formulaContract = BancorFormula(formula);
        if (formulaContract.newFormula() != _formula)
            throw;

        formula = _formula;
        return true;
    }

    /*
        updates the crowdsale contract address (managed stage only)
        can only be called by the token owner

        _crowdsale     new crowdsale contract address
    */
    function setCrowdsale(address _crowdsale) public onlyOwner returns (bool success) {
        if (stage != Stage.Managed) // validate state
            throw;

        crowdsale = _crowdsale;
        return true;
    }

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

    /*
        defines a new reserve for the token (managed stage only)
        can only be called by the token owner

        _token  address of the reserve token
        _ratio  constant reserve ratio, 1-99
    */
    function addReserve(address _token, uint8 _ratio) public onlyOwner returns (bool success) {
        if (reserveRatioOf[_token] != 0 || _ratio < 1 || _ratio > 99) // validate input
            throw;
        if (stage != Stage.Managed) // validate state
            throw;

        reserveRatioOf[_token] = _ratio;
        reserveTokens.push(_token);
        dispatchUpdate();
        return true;
    }

    /*
        increases the token supply and sends the new tokens to an account
        can only be called by the token owner (in managed stage only) or the crowdsale contract (in crowdsale stage only)

        _to         account to receive the new amount
        _amount     amount to increase the supply by
    */
    function issue(address _to, uint256 _amount) public onlyManager returns (bool success) {
        if (totalSupply + _amount < totalSupply) // supply overflow protection
            throw;
        if (balanceOf[_to] + _amount < balanceOf[_to]) // target account balance overflow protection
            throw;

        totalSupply += _amount;
        balanceOf[_to] += _amount;
        dispatchUpdate();
        dispatchTransfer(this, _to, _amount);
        return true;
    }

    /*
        Removes tokens from an account and decreases the token supply
        can only be called by the token owner (in managed stage only) or the crowdsale contract (in crowdsale stage only)

        _from       account to remove the new amount from
        _amount     amount to decrease the supply by
    */
    function destroy(address _from, uint256 _amount) public onlyManager returns (bool success) {
        if (_amount > totalSupply) // negative supply protection
            throw;
        if (_amount > balanceOf[_from]) // target account negative balance protection
            throw;

        totalSupply -= _amount;
        balanceOf[_from] -= _amount;
        dispatchUpdate();
        dispatchTransfer(_from, this, _amount);
        return true;
    }

    /*
        withdraws tokens from the reserve and sends them to an account
        can only be called by the token owner (in managed stage only) or the crowdsale contract (in crowdsale stage only)

        _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 onlyManager returns (bool success) {
        if (reserveRatioOf[_reserveToken] == 0 || _amount == 0) // validate input
            throw;

        ReserveToken reserveToken = ReserveToken(_reserveToken);
        return reserveToken.transfer(_to, _amount);
    }

    /*
        starts the crowdsale stage (managed stage only)
        can only be called by the token owner
    */
    function startCrowdsale() public onlyOwner returns (bool success) {
        if (stage != Stage.Managed || crowdsale == 0x0 || reserveTokens.length == 0) // validate state
            throw;

        stage = Stage.Crowdsale;
        dispatchUpdate();
        return true;
    }

    /*
        starts the traded stage
        can only be called by the token owner (in managed stage only) or the crowdsale contract (in crowdsale stage only)
    */
    function startTrading() public onlyManager returns (bool success) {
        if (totalSupply == 0) // validate state
            throw;

        // make sure that there's balance in all the reserves
        for (uint16 i = 0; i < reserveTokens.length; ++i) {
            ReserveToken reserveToken = ReserveToken(reserveTokens[i]);
            if (reserveToken.balanceOf(this) == 0)
                throw;
        }

        stage = Stage.Traded;
        dispatchUpdate();
        return true;
    }

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

        _reserveToken   reserve token contract address
        _depositAmount  amount to deposit (in the reserve token)
        _minimumValue   if the conversion results in a value smaller than this value, it is cancelled
    */
    function buy(address _reserveToken, uint256 _depositAmount, uint256 _minimumValue) public returns (uint256 value) {
        uint8 reserveRatio = reserveRatioOf[_reserveToken];
        if (reserveRatio == 0 || _depositAmount == 0) // validate input
            throw;
        if (stage != Stage.Traded) // validate state
            throw;

        ReserveToken reserveToken = ReserveToken(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);

        BancorFormula formulaContract = BancorFormula(formula);
        value = formulaContract.calculatePurchaseValue(totalSupply, reserveBalance, reserveRatio, _depositAmount);
        if (value == 0 || value < _minimumValue) // trade gave nothing in return or didn't return a value that meets the minimum requested value
            throw;
        if (totalSupply + value < totalSupply) // supply overflow protection
            throw;
        if (!reserveToken.transferFrom(msg.sender, this, _depositAmount)) // can't withdraw funds from the reserve token
            throw;

        uint256 startSupply = totalSupply;
        totalSupply += value;
        balanceOf[msg.sender] += value;
        dispatchConversion(_reserveToken, msg.sender, true, startSupply, reserveBalance, value, _depositAmount);
        return value;
    }

    /*
        sells the token for one of its reserve tokens

        _reserveToken   reserve token contract address
        _sellAmount     amount to sell (in the token)
        _minimumValue   if the conversion results in a value smaller than this value, it is cancelled
    */
    function sell(address _reserveToken, uint256 _sellAmount, uint256 _minimumValue) public returns (uint256 value) {
        uint8 reserveRatio = reserveRatioOf[_reserveToken];
        if (reserveRatio == 0 || _sellAmount == 0) // validate input
            throw;
        if (stage != Stage.Traded) // validate state
            throw;
        if (balanceOf[msg.sender] < _sellAmount) // balance check
            throw;

        ReserveToken reserveToken = ReserveToken(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);

        BancorFormula formulaContract = BancorFormula(formula);
        value = formulaContract.calculateSaleValue(totalSupply, reserveBalance, reserveRatio, _sellAmount);
        if (value == 0 || value < _minimumValue) // trade gave nothing in return or didn't return a value that meets the minimum requested value
            throw;
        if (reserveBalance <= value) // trade will deplete the reserve
            throw;

        uint256 startSupply = totalSupply;
        totalSupply -= _sellAmount;
        balanceOf[msg.sender] -= _sellAmount;
        if (!reserveToken.transfer(msg.sender, value)) // can't transfer funds to the caller
            throw;

        // if the supply was totally depleted, return to managed stage
        if (totalSupply == 0)
            stage = Stage.Managed;

        dispatchConversion(_reserveToken, msg.sender, false, startSupply, reserveBalance, _sellAmount, value);
        return value;
    }

    // ERC20 standard methods

    // send coins
    function transfer(address _to, uint256 _value) public returns (bool success) {
        if (balanceOf[msg.sender] < _value) // balance check
            throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) // overflow protection
            throw;

        balanceOf[msg.sender] -= _value;
        if (_to == address(this)) // transferring to the contract address destroys tokens
            totalSupply -= _value;
        else
            balanceOf[_to] += _value;

        dispatchTransfer(msg.sender, _to, _value);
        return true;
    }

    // allow another account/contract to spend some tokens on your behalf
    function approve(address _spender, uint256 _value) public returns (bool success) {
        // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
        if (_value != 0 && allowance[msg.sender][_spender] != 0)
            throw;

        allowance[msg.sender][_spender] = _value;

        Approval(msg.sender, _spender, _value);
        if (events == 0x0)
            return true;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.tokenApproval(msg.sender, _spender, _value);
        return true;
    }

    // an account/contract attempts to get the coins
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        if (balanceOf[_from] < _value) // balance check
            throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) // overflow protection
            throw;
        if (_value > allowance[_from][msg.sender]) // allowance check
            throw;

        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        allowance[_from][msg.sender] -= _value;

        dispatchTransfer(_from, _to, _value);
        return true;
    }

    // utility

    function dispatchUpdate() private {
        Update();
        if (events == 0x0)
            return;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.tokenUpdate();
    }

    function dispatchTransfer(address _from, address _to, uint256 _value) private {
        Transfer(_from, _to, _value);
        if (events == 0x0)
            return;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.tokenTransfer(_from, _to, _value);
    }

    function dispatchConversion(address _reserveToken, address _trader, bool _isPurchase,
                                uint256 _totalSupply, uint256 _reserveBalance, uint256 _tokenAmount, uint256 _reserveAmount) private {
        Conversion(_reserveToken, _trader, _isPurchase, _totalSupply, _reserveBalance, _tokenAmount, _reserveAmount);
        if (events == 0x0)
            return;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.tokenConversion(_reserveToken, _trader, _isPurchase, _totalSupply, _reserveBalance, _tokenAmount, _reserveAmount);
    }

    // fallback

    function() {
        throw;
    }
}

pragma solidity ^0.4.9;
import "owned.sol";

/*
    Open issues:
    - throw vs. return value?
    - possibly add modifiers for each stage
    - possibly create a shared standard token contract and inherit from it, both for the BancorToken and for the BancorEtherToken
    - add miner abuse protection
    - allow exchanging between 2 reserve tokens directly? can be done through a 3rd party contract
    - number of reserve tokens is limited to 256 (uint8) - is that enough?
    - startTrading - looping over the reserve - can run out of gas. Possibly split it and do it as a multi-step process
    - approve - to minimize the risk of the approve/transferFrom attack vector
                (see https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/), approve has to be called twice
                in 2 separate transactions - once to change the allowance to 0 and secondly to change it to the new allowance value.
                Also relevant to the BancorEtherToken
*/

// interfaces

contract ReserveToken { // any ERC20 standard token
    function balanceOf(address _owner) public constant returns (uint256 balance);
    function transfer(address _to, uint256 _value) public returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
}

contract BancorFormula {
    function calculatePurchaseValue(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _depositAmount) public constant returns (uint256 value);
    function calculateSaleValue(uint256 _supply, uint256 _reserveBalance, uint16 _reserveRatio, uint256 _sellAmount) public constant returns (uint256 value);
    function newFormula() public constant returns (address newFormula);
}

contract BancorEvents {
    function newToken() public;
    function tokenUpdate() public;
    function tokenTransfer(address _from, address _to, uint256 _value) public;
    function tokenApproval(address _owner, address _spender, uint256 _value) public;
    function tokenConversion(address _reserveToken, address _trader, bool _isPurchase, uint256 _totalSupply,
                             uint256 _reserveBalance, uint256 _tokenAmount, uint256 _reserveAmount) public;
}

/*
    Bancor Token v0.2
*/
contract BancorToken is owned {
    enum Stage { Managed, Crowdsale, Traded }

    string public standard = 'Token 0.1';
    string public name = "";
    string public symbol = "";                          // 1-6 characters
    uint8 public numDecimalUnits = 0;                   // for display purposes only
    uint256 public totalSupply = 0;
    address public formula = 0x0;                       // bancor calculation formula contract address
    address public events = 0x0;                        // bancor events contract address
    address public crowdsale = 0x0;                     // crowdsale contract address
    Stage public stage = Stage.Managed;                 // token stage
    address[] public reserveTokens;                     // ERC20 standard token addresses
    mapping (address => uint8) public reserveRatioOf;   // token addresses -> constant reserve ratio, 1-99
    mapping (address => uint256) public balanceOf;
    mapping (address => mapping (address => uint256)) public allowance;

    // events, can be used to listen to the contract directly, as opposed to through the events contract
    event Update();
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);
    event Conversion(address indexed _reserveToken, address indexed _trader, bool _isPurchase,
                     uint256 _totalSupply, uint256 _reserveBalance, uint256 _tokenAmount, uint256 _reserveAmount);

    /*
        _name               token name
        _symbol             token short symbol
        _numDecimalUnits    for display purposes only
        _formula            address of a bancor formula contract
        _events             optional, address of a bancor events contract
    */
    function BancorToken(string _name, string _symbol, uint8 _numDecimalUnits, address _formula, address _events) {
        if (bytes(_name).length == 0 || bytes(_symbol).length < 1 || bytes(_symbol).length > 6 || _formula == 0x0) // validate input
            throw;

        name = _name;
        symbol = _symbol;
        numDecimalUnits = _numDecimalUnits;
        formula = _formula;
        events = _events;

        if (events == 0x0)
            return;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.newToken();
    }

    // allows executing a function by the owner in managed stage or by the crowdsale contract in crowdsale stage
    modifier onlyManager {
        if (stage == Stage.Traded ||
            stage == Stage.Managed && msg.sender != owner ||
            stage == Stage.Crowdsale && msg.sender != crowdsale) // validate state & permissions
            throw;
        _;
    }

    /*
        updates the bancor calculation formula contract address
        can only be called by the token owner

        the owner can only update the formula to a new one approved by the current formula's owner

        _formula     new formula contract address
    */
    function setFormula(address _formula) public onlyOwner returns (bool success) {
        BancorFormula formulaContract = BancorFormula(formula);
        if (formulaContract.newFormula() != _formula)
            throw;

        formula = _formula;
        return true;
    }

    /*
        updates the crowdsale contract address (managed stage only)
        can only be called by the token owner

        _crowdsale     new crowdsale contract address
    */
    function setCrowdsale(address _crowdsale) public onlyOwner returns (bool success) {
        if (stage != Stage.Managed) // validate state
            throw;

        crowdsale = _crowdsale;
        return true;
    }

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

    /*
        defines a new reserve for the token (managed stage only)
        can only be called by the token owner

        _token  address of the reserve token
        _ratio  constant reserve ratio, 1-99
    */
    function addReserve(address _token, uint8 _ratio) public onlyOwner returns (bool success) {
        if (reserveRatioOf[_token] != 0 || _ratio < 1 || _ratio > 99) // validate input
            throw;
        if (stage != Stage.Managed) // validate state
            throw;

        reserveRatioOf[_token] = _ratio;
        reserveTokens.push(_token);
        dispatchUpdate();
        return true;
    }

    /*
        increases the token supply and sends the new tokens to an account
        can only be called by the token owner (in managed stage only) or the crowdsale contract (in crowdsale stage only)

        _to         account to receive the new amount
        _amount     amount to increase the supply by
    */
    function issue(address _to, uint256 _amount) public onlyManager returns (bool success) {
        if (totalSupply + _amount < totalSupply) // supply overflow protection
            throw;
        if (balanceOf[_to] + _amount < balanceOf[_to]) // target account balance overflow protection
            throw;

        totalSupply += _amount;
        balanceOf[_to] += _amount;
        dispatchUpdate();
        dispatchTransfer(this, _to, _amount);
        return true;
    }

    /*
        Removes tokens from an account and decreases the token supply
        can only be called by the token owner (in managed stage only) or the crowdsale contract (in crowdsale stage only)

        _from       account to remove the new amount from
        _amount     amount to decrease the supply by
    */
    function destroy(address _from, uint256 _amount) public onlyManager returns (bool success) {
        if (_amount > totalSupply) // negative supply protection
            throw;
        if (_amount > balanceOf[_from]) // target account negative balance protection
            throw;

        totalSupply -= _amount;
        balanceOf[_from] -= _amount;
        dispatchUpdate();
        dispatchTransfer(_from, this, _amount);
        return true;
    }

    /*
        withdraws tokens from the reserve and sends them to an account
        can only be called by the token owner (in managed stage only) or the crowdsale contract (in crowdsale stage only)

        _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 onlyManager returns (bool success) {
        if (reserveRatioOf[_reserveToken] == 0 || _amount == 0) // validate input
            throw;

        ReserveToken reserveToken = ReserveToken(_reserveToken);
        return reserveToken.transfer(_to, _amount);
    }

    /*
        starts the crowdsale stage (managed stage only)
        can only be called by the token owner
    */
    function startCrowdsale() public onlyOwner returns (bool success) {
        if (stage != Stage.Managed || crowdsale == 0x0 || reserveTokens.length == 0) // validate state
            throw;

        stage = Stage.Crowdsale;
        dispatchUpdate();
        return true;
    }

    /*
        starts the traded stage
        can only be called by the token owner (in managed stage only) or the crowdsale contract (in crowdsale stage only)
    */
    function startTrading() public onlyManager returns (bool success) {
        if (totalSupply == 0) // validate state
            throw;

        // make sure that there's balance in all the reserves
        for (uint8 i = 0; i < reserveTokens.length; ++i) {
            ReserveToken reserveToken = ReserveToken(reserveTokens[i]);
            if (reserveToken.balanceOf(this) == 0)
                throw;
        }

        stage = Stage.Traded;
        dispatchUpdate();
        return true;
    }

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

        _reserveToken   reserve token contract address
        _depositAmount  amount to deposit (in the reserve token)
        _minimumValue   if the conversion results in a value smaller than this value, it is cancelled
    */
    function buy(address _reserveToken, uint256 _depositAmount, uint256 _minimumValue) public returns (uint256 value) {
        uint8 reserveRatio = reserveRatioOf[_reserveToken];
        if (reserveRatio == 0 || _depositAmount == 0) // validate input
            throw;
        if (stage != Stage.Traded) // validate state
            throw;

        ReserveToken reserveToken = ReserveToken(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);

        BancorFormula formulaContract = BancorFormula(formula);
        value = formulaContract.calculatePurchaseValue(totalSupply, reserveBalance, reserveRatio, _depositAmount);
        if (value == 0 || value < _minimumValue) // trade gave nothing in return or didn't return a value that meets the minimum requested value
            throw;
        if (totalSupply + value < totalSupply) // supply overflow protection
            throw;
        if (!reserveToken.transferFrom(msg.sender, this, _depositAmount)) // can't withdraw funds from the reserve token
            throw;

        uint256 startSupply = totalSupply;
        totalSupply += value;
        balanceOf[msg.sender] += value;
        dispatchConversion(_reserveToken, msg.sender, true, startSupply, reserveBalance, value, _depositAmount);
        return value;
    }

    /*
        sells the token for one of its reserve tokens

        _reserveToken   reserve token contract address
        _sellAmount     amount to sell (in the token)
        _minimumValue   if the conversion results in a value smaller than this value, it is cancelled
    */
    function sell(address _reserveToken, uint256 _sellAmount, uint256 _minimumValue) public returns (uint256 value) {
        uint8 reserveRatio = reserveRatioOf[_reserveToken];
        if (reserveRatio == 0 || _sellAmount == 0) // validate input
            throw;
        if (stage != Stage.Traded) // validate state
            throw;
        if (balanceOf[msg.sender] < _sellAmount) // balance check
            throw;

        ReserveToken reserveToken = ReserveToken(_reserveToken);
        uint256 reserveBalance = reserveToken.balanceOf(this);

        BancorFormula formulaContract = BancorFormula(formula);
        value = formulaContract.calculateSaleValue(totalSupply, reserveBalance, reserveRatio, _sellAmount);
        if (value == 0 || value < _minimumValue) // trade gave nothing in return or didn't return a value that meets the minimum requested value
            throw;
        if (reserveBalance <= value) // trade will deplete the reserve
            throw;

        uint256 startSupply = totalSupply;
        totalSupply -= _sellAmount;
        balanceOf[msg.sender] -= _sellAmount;
        if (!reserveToken.transfer(msg.sender, value)) // can't transfer funds to the caller
            throw;

        // if the supply was totally depleted, return to managed stage
        if (totalSupply == 0)
            stage = Stage.Managed;

        dispatchConversion(_reserveToken, msg.sender, false, startSupply, reserveBalance, _sellAmount, value);
        return value;
    }

    // ERC20 standard methods

    // send coins
    function transfer(address _to, uint256 _value) public returns (bool success) {
        if (balanceOf[msg.sender] < _value) // balance check
            throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) // overflow protection
            throw;

        balanceOf[msg.sender] -= _value;
        if (_to == address(this)) // transferring to the contract address destroys tokens
            totalSupply -= _value;
        else
            balanceOf[_to] += _value;

        dispatchTransfer(msg.sender, _to, _value);
        return true;
    }

    // allow another account/contract to spend some tokens on your behalf
    function approve(address _spender, uint256 _value) public returns (bool success) {
        // if the allowance isn't 0, it can only be updated to 0 to prevent an allowance change immediately after withdrawal
        if (_value != 0 && allowance[msg.sender][_spender] != 0)
            throw;

        allowance[msg.sender][_spender] = _value;

        Approval(msg.sender, _spender, _value);
        if (events == 0x0)
            return true;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.tokenApproval(msg.sender, _spender, _value);
        return true;
    }

    // an account/contract attempts to get the coins
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        if (balanceOf[_from] < _value) // balance check
            throw;
        if (balanceOf[_to] + _value < balanceOf[_to]) // overflow protection
            throw;
        if (_value > allowance[_from][msg.sender]) // allowance check
            throw;

        balanceOf[_from] -= _value;
        balanceOf[_to] += _value;
        allowance[_from][msg.sender] -= _value;

        dispatchTransfer(_from, _to, _value);
        return true;
    }

    // utility

    function dispatchUpdate() private {
        Update();
        if (events == 0x0)
            return;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.tokenUpdate();
    }

    function dispatchTransfer(address _from, address _to, uint256 _value) private {
        Transfer(_from, _to, _value);
        if (events == 0x0)
            return;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.tokenTransfer(_from, _to, _value);
    }

    function dispatchConversion(address _reserveToken, address _trader, bool _isPurchase,
                                uint256 _totalSupply, uint256 _reserveBalance, uint256 _tokenAmount, uint256 _reserveAmount) private {
        Conversion(_reserveToken, _trader, _isPurchase, _totalSupply, _reserveBalance, _tokenAmount, _reserveAmount);
        if (events == 0x0)
            return;

        BancorEvents eventsContract = BancorEvents(events);
        eventsContract.tokenConversion(_reserveToken, _trader, _isPurchase, _totalSupply, _reserveBalance, _tokenAmount, _reserveAmount);
    }

    // fallback

    function() {
        throw;
    }
}


Add control tests.

Add control tests.

pragma solidity ^0.4.11;

import "ds-test/test.sol";

import "./blobstore_ipfs_sha256.sol";
import "./blobstore_ipfs_sha256_proxy.sol";

/**
 * @title BlobStoreTest
 * @author Jonathan Brown <jbrown@bluedroplet.com>
 */
contract BlobStoreTest is DSTest {

    byte constant UPDATABLE = 0x01;           // True if the blob is updatable. After creation can only be disabled.
    byte constant ENFORCE_REVISIONS = 0x02;   // True if the blob is enforcing revisions. After creation can only be enabled.
    byte constant RETRACTABLE = 0x04;         // True if the blob can be retracted. After creation can only be disabled.
    byte constant TRANSFERABLE = 0x08;        // True if the blob be transfered to another user or disowned. After creation can only be disabled.
    byte constant ANONYMOUS = 0x10;           // True if the blob should not have an owner.

    BlobStoreRegistry blobStoreRegistry;
    BlobStoreIpfsSha256 blobStore;
    BlobStoreIpfsSha256Proxy blobStoreProxy;

    function setUp() {
        blobStoreRegistry = new BlobStoreRegistry();
        blobStore = new BlobStoreIpfsSha256(blobStoreRegistry);
        blobStoreProxy = new BlobStoreIpfsSha256Proxy(blobStore);
    }

    function testControlCreateSameIpfsHashAndNonce() {
        blobStore.create(0, 0x1234, 0);
        blobStore.create(0, 0x1234, 1);
        blobStore.create(0, 0x2345, 0);
    }

    function testFailCreateSameIpfsHashAndNonce() {
        blobStore.create(0, 0x1234, 0);
        blobStore.create(0, 0x1234, 0);
    }

    function testCreate() {
        bytes20 blobId0 = blobStore.create(0, 0x1234, 0);
        assert(blobStore.getExists(blobId0));
        assertEq(blobStore.getFlags(blobId0), 0);
        assertEq(blobStore.getOwner(blobId0), this);
        assertEq(blobStore.getRevisionCount(blobId0), 1);
        assert(!blobStore.getUpdatable(blobId0));
        assert(!blobStore.getEnforceRevisions(blobId0));
        assert(!blobStore.getRetractable(blobId0));
        assert(!blobStore.getTransferable(blobId0));
        assertEq(blobStore.getRevisionIpfsHash(blobId0, 0), 0x1234);

        bytes20 blobId1 = blobStore.create(UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS, 0x1234, 1);
        assert(blobStore.getExists(blobId1));
        assertEq(blobStore.getFlags(blobId1), UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS);
        assertEq(blobStore.getOwner(blobId1), 0);
        assertEq(blobStore.getRevisionCount(blobId1), 1);
        assert(blobStore.getUpdatable(blobId1));
        assert(blobStore.getEnforceRevisions(blobId1));
        assert(blobStore.getRetractable(blobId1));
        assert(blobStore.getTransferable(blobId1));
        assertEq(blobStore.getRevisionIpfsHash(blobId1, 0), 0x1234);

        bytes20 blobId2 = blobStore.create(UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS, 0x2345, 0);
        assert(blobStore.getExists(blobId2));
        assertEq(blobStore.getFlags(blobId2), UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS);
        assertEq(blobStore.getOwner(blobId2), 0);
        assertEq(blobStore.getRevisionCount(blobId2), 1);
        assert(blobStore.getUpdatable(blobId2));
        assert(blobStore.getEnforceRevisions(blobId2));
        assert(blobStore.getRetractable(blobId2));
        assert(blobStore.getTransferable(blobId2));
        assertEq(blobStore.getRevisionIpfsHash(blobId2, 0), 0x2345);

        assert(blobId0 != blobId1);
        assert(blobId0 != blobId2);
        assert(blobId1 != blobId2);
    }

    function testControlCreateNewRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
    }

    function testFailCreateNewRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStoreProxy.createNewRevision(blobId, 0x2345);
    }

    function testControlCreateNewRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
    }

    function testFailCreateNewRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
    }

    function testCreateNewRevision() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        uint revisionId = blobStore.createNewRevision(blobId, 0x2345);
        assertEq(revisionId, 1);
        assertEq(blobStore.getRevisionCount(blobId), 2);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 1), 0x2345);
    }

    function testControlUpdateLatestRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.updateLatestRevision(blobId, 0x2345);
    }

    function testFailUpdateLatestRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStoreProxy.updateLatestRevision(blobId, 0x2345);
    }

    function testControlUpdateLatestRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.updateLatestRevision(blobId, 0x2345);
    }

    function testFailUpdateLatestRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.updateLatestRevision(blobId, 0x2345);
    }

    function testControlUpdateLatestRevisionEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.updateLatestRevision(blobId, 0x2345);
    }

    function testFailUpdateLatestRevisionEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        blobStore.updateLatestRevision(blobId, 0x2345);
    }

    function testUpdateLatestRevision() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        blobStore.updateLatestRevision(blobId, 0x2345);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x2345);
    }

    function testControlRetractLatestRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.retractLatestRevision(blobId);
    }

    function testFailRetractLatestRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStoreProxy.retractLatestRevision(blobId);
    }

    function testControlRetractLatestRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.retractLatestRevision(blobId);
    }

    function testFailRetractLatestRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.setNotUpdatable(blobId);
        blobStore.retractLatestRevision(blobId);
    }

    function testControlRetractLatestRevisionEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.retractLatestRevision(blobId);
    }

    function testFailRetractLatestRevisionEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.retractLatestRevision(blobId);
    }

    function testControlRetractLatestRevisionDoesntHaveAdditionalRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.retractLatestRevision(blobId);
    }

    function testFailRetractLatestRevisionDoesntHaveAdditionalRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.retractLatestRevision(blobId);
    }

    function testRetractLatestRevision() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.createNewRevision(blobId, 0x3456);
        assertEq(blobStore.getRevisionCount(blobId), 3);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 1), 0x2345);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 2), 0x3456);
        blobStore.retractLatestRevision(blobId);
        assertEq(blobStore.getRevisionCount(blobId), 2);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 1), 0x2345);
    }

    function testControlRestartNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.restart(blobId, 0x2345);
    }

    function testFailRestartNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStoreProxy.restart(blobId, 0x2345);
    }

    function testControlRestartNotUpdatable() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.restart(blobId, 0x2345);
    }

    function testFailRestartNotUpdatable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.restart(blobId, 0x2345);
    }

    function testControlRestartEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.restart(blobId, 0x2345);
    }

    function testFailRestartEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        blobStore.restart(blobId, 0x2345);
    }

    function testRestart() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.createNewRevision(blobId, 0x3456);
        assertEq(blobStore.getRevisionCount(blobId), 3);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 1), 0x2345);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 2), 0x3456);
        blobStore.restart(blobId, 0x4567);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x4567);
    }

    function testControlRetractNotOwner() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        blobStore.retract(blobId);
    }

    function testFailRetractNotOwner() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        blobStoreProxy.retract(blobId);
    }

    function testControlRetractNotRetractable() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        blobStore.retract(blobId);
    }

    function testFailRetractNotRetractable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.retract(blobId);
    }

    function testRetract() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        blobStore.retract(blobId);
        assert(!blobStore.getExists(blobId));
    }

    function testControlTransferEnableNotTransferable() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
    }

    function testFailTransferEnableNotTransferable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
    }

    function testControlTransferDisableNotEnabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
        blobStoreProxy.transferDisable(blobId);
    }

    function testFailTransferDisableNotEnabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferDisable(blobId);
    }

    function testControlTransferNotTransferable() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
    }

    function testFailTransferNotTransferable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
    }

    function testControlTransferNotEnabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
    }

    function testFailTransferNotEnabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStore.transfer(blobId, blobStoreProxy);
    }

    function testControlTransferDisabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
    }

    function testFailTransferDisabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
        blobStoreProxy.transferDisable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
    }

    function testTransfer() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        blobStoreProxy.transferEnable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
        assertEq(blobStore.getOwner(blobId), blobStoreProxy);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
    }

    function testControlDisownNotOwner() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStore.disown(blobId);
    }

    function testFailDisownNotOwner() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.disown(blobId);
    }

    function testControlDisownNotTransferable() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStore.disown(blobId);
    }

    function testFailDisownNotTransferable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.disown(blobId);
    }

    function testDisown() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        assertEq(blobStore.getOwner(blobId), this);
        blobStore.disown(blobId);
        assertEq(blobStore.getOwner(blobId), 0);
    }

    function testControlSetNotUpdatableNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.setNotUpdatable(blobId);
    }

    function testFailSetNotUpdatableNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStoreProxy.setNotUpdatable(blobId);
    }

    function testSetNotUpdatable() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        assert(blobStore.getUpdatable(blobId));
        blobStore.setNotUpdatable(blobId);
        assert(!blobStore.getUpdatable(blobId));
    }

    function testControlSetEnforceRevisionsNotOwner() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.setEnforceRevisions(blobId);
    }

    function testFailSetEnforceRevisionsNotOwner() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStoreProxy.setEnforceRevisions(blobId);
    }

    function testSetEnforceRevisions() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        assert(!blobStore.getEnforceRevisions(blobId));
        blobStore.setEnforceRevisions(blobId);
        assert(blobStore.getEnforceRevisions(blobId));
    }

    function testControlSetNotRetractableNotOwner() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        blobStore.setNotRetractable(blobId);
    }

    function testFailSetNotRetractableNotOwner() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        blobStoreProxy.setNotRetractable(blobId);
    }

    function testSetNotRetractable() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        assert(blobStore.getRetractable(blobId));
        blobStore.setNotRetractable(blobId);
        assert(!blobStore.getRetractable(blobId));
    }

    function testControlSetNotTransferableNotOwner() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStore.setNotTransferable(blobId);
    }

    function testFailSetNotTransferableNotOwner() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.setNotTransferable(blobId);
    }

    function testSetNotTransferable() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        assert(blobStore.getTransferable(blobId));
        blobStore.setNotTransferable(blobId);
        assert(!blobStore.getTransferable(blobId));
    }

}

pragma solidity ^0.4.11;

import "ds-test/test.sol";

import "./blobstore_ipfs_sha256.sol";
import "./blobstore_ipfs_sha256_proxy.sol";

/**
 * @title BlobStoreTest
 * @author Jonathan Brown <jbrown@bluedroplet.com>
 */
contract BlobStoreTest is DSTest {

    byte constant UPDATABLE = 0x01;           // True if the blob is updatable. After creation can only be disabled.
    byte constant ENFORCE_REVISIONS = 0x02;   // True if the blob is enforcing revisions. After creation can only be enabled.
    byte constant RETRACTABLE = 0x04;         // True if the blob can be retracted. After creation can only be disabled.
    byte constant TRANSFERABLE = 0x08;        // True if the blob be transfered to another user or disowned. After creation can only be disabled.
    byte constant ANONYMOUS = 0x10;           // True if the blob should not have an owner.

    BlobStoreRegistry blobStoreRegistry;
    BlobStoreIpfsSha256 blobStore;
    BlobStoreIpfsSha256Proxy blobStoreProxy;

    function setUp() {
        blobStoreRegistry = new BlobStoreRegistry();
        blobStore = new BlobStoreIpfsSha256(blobStoreRegistry);
        blobStoreProxy = new BlobStoreIpfsSha256Proxy(blobStore);
    }

    function testFailCreateSameIpfsHashAndNonce() {
        blobStore.create(0, 0x1234, 0);
        blobStore.create(0, 0x1234, 0);
    }

    function testCreate() {
        bytes20 blobId0 = blobStore.create(0, 0x1234, 0);
        assert(blobStore.getExists(blobId0));
        assertEq(blobStore.getFlags(blobId0), 0);
        assertEq(blobStore.getOwner(blobId0), this);
        assertEq(blobStore.getRevisionCount(blobId0), 1);
        assert(!blobStore.getUpdatable(blobId0));
        assert(!blobStore.getEnforceRevisions(blobId0));
        assert(!blobStore.getRetractable(blobId0));
        assert(!blobStore.getTransferable(blobId0));
        assertEq(blobStore.getRevisionIpfsHash(blobId0, 0), 0x1234);

        bytes20 blobId1 = blobStore.create(UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS, 0x1234, 1);
        assert(blobStore.getExists(blobId1));
        assertEq(blobStore.getFlags(blobId1), UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS);
        assertEq(blobStore.getOwner(blobId1), 0);
        assertEq(blobStore.getRevisionCount(blobId1), 1);
        assert(blobStore.getUpdatable(blobId1));
        assert(blobStore.getEnforceRevisions(blobId1));
        assert(blobStore.getRetractable(blobId1));
        assert(blobStore.getTransferable(blobId1));
        assertEq(blobStore.getRevisionIpfsHash(blobId1, 0), 0x1234);

        bytes20 blobId2 = blobStore.create(UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS, 0x2345, 0);
        assert(blobStore.getExists(blobId2));
        assertEq(blobStore.getFlags(blobId2), UPDATABLE | ENFORCE_REVISIONS | RETRACTABLE | TRANSFERABLE | ANONYMOUS);
        assertEq(blobStore.getOwner(blobId2), 0);
        assertEq(blobStore.getRevisionCount(blobId2), 1);
        assert(blobStore.getUpdatable(blobId2));
        assert(blobStore.getEnforceRevisions(blobId2));
        assert(blobStore.getRetractable(blobId2));
        assert(blobStore.getTransferable(blobId2));
        assertEq(blobStore.getRevisionIpfsHash(blobId2, 0), 0x2345);

        assert(blobId0 != blobId1);
        assert(blobId0 != blobId2);
        assert(blobId1 != blobId2);
    }

    function testFailCreateNewRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStoreProxy.createNewRevision(blobId, 0x2345);
    }

    function testFailCreateNewRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
    }

    function testCreateNewRevision() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        uint revisionId = blobStore.createNewRevision(blobId, 0x2345);
        assertEq(revisionId, 1);
        assertEq(blobStore.getRevisionCount(blobId), 2);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 1), 0x2345);
    }

    function testFailUpdateLatestRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStoreProxy.updateLatestRevision(blobId, 0x2345);
    }

    function testFailUpdateLatestRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.updateLatestRevision(blobId, 0x2345);
    }

    function testFailUpdateLatestRevisionEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        blobStore.updateLatestRevision(blobId, 0x2345);
    }

    function testUpdateLatestRevision() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        blobStore.updateLatestRevision(blobId, 0x2345);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x2345);
    }

    function testFailRetractLatestRevisionNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStoreProxy.retractLatestRevision(blobId);
    }

    function testFailRetractLatestRevisionNotUpdatable() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.setNotUpdatable(blobId);
        blobStore.retractLatestRevision(blobId);
    }

    function testFailRetractLatestRevisionEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.retractLatestRevision(blobId);
    }

    function testFailRetractLatestRevisionDoesntHaveAdditionalRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.retractLatestRevision(blobId);
    }

    function testRetractLatestRevision() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.createNewRevision(blobId, 0x3456);
        assertEq(blobStore.getRevisionCount(blobId), 3);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 1), 0x2345);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 2), 0x3456);
        blobStore.retractLatestRevision(blobId);
        assertEq(blobStore.getRevisionCount(blobId), 2);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 1), 0x2345);
    }

    function testFailRestartNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStoreProxy.restart(blobId, 0x2345);
    }

    function testFailRestartNotUpdatable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.restart(blobId, 0x2345);
    }

    function testFailRestartEnforceRevisions() {
        bytes20 blobId = blobStore.create(UPDATABLE | ENFORCE_REVISIONS, 0x1234, 0);
        blobStore.restart(blobId, 0x2345);
    }

    function testRestart() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStore.createNewRevision(blobId, 0x2345);
        blobStore.createNewRevision(blobId, 0x3456);
        assertEq(blobStore.getRevisionCount(blobId), 3);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 1), 0x2345);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 2), 0x3456);
        blobStore.restart(blobId, 0x4567);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x4567);
    }

    function testFailRetractNotOwner() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        blobStoreProxy.retract(blobId);
    }

    function testFailRetractNotRetractable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.retract(blobId);
    }

    function testRetract() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        blobStore.retract(blobId);
        assert(!blobStore.getExists(blobId));
    }

    function testFailTransferEnableNotTransferable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
    }

    function testFailTransferDisableNotEnabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferDisable(blobId);
    }

    function testFailTransferNotTransferable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
    }

    function testFailTransferNotEnabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStore.transfer(blobId, blobStoreProxy);
    }

    function testFailTransferDisabled() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.transferEnable(blobId);
        blobStoreProxy.transferDisable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
        assertEq(blobStore.getOwner(blobId), blobStoreProxy);
    }

    function testTransfer() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
        blobStoreProxy.transferEnable(blobId);
        blobStore.transfer(blobId, blobStoreProxy);
        assertEq(blobStore.getOwner(blobId), blobStoreProxy);
        assertEq(blobStore.getRevisionCount(blobId), 1);
        assertEq(blobStore.getRevisionIpfsHash(blobId, 0), 0x1234);
    }

    function testFailDisownNotOwner() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.disown(blobId);
    }

    function testFailDisownNotTransferable() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStore.disown(blobId);
    }

    function testDisown() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        assertEq(blobStore.getOwner(blobId), this);
        blobStore.disown(blobId);
        assertEq(blobStore.getOwner(blobId), 0);
    }

    function testFailSetNotUpdatableNotOwner() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        blobStoreProxy.setNotUpdatable(blobId);
    }

    function testSetNotUpdatable() {
        bytes20 blobId = blobStore.create(UPDATABLE, 0x1234, 0);
        assert(blobStore.getUpdatable(blobId));
        blobStore.setNotUpdatable(blobId);
        assert(!blobStore.getUpdatable(blobId));
    }

    function testFailSetEnforceRevisionsNotOwner() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        blobStoreProxy.setEnforceRevisions(blobId);
    }

    function testSetEnforceRevisions() {
        bytes20 blobId = blobStore.create(0, 0x1234, 0);
        assert(!blobStore.getEnforceRevisions(blobId));
        blobStore.setEnforceRevisions(blobId);
        assert(blobStore.getEnforceRevisions(blobId));
    }

    function testFailSetNotRetractableNotOwner() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        blobStoreProxy.setNotRetractable(blobId);
    }

    function testSetNotRetractable() {
        bytes20 blobId = blobStore.create(RETRACTABLE, 0x1234, 0);
        assert(blobStore.getRetractable(blobId));
        blobStore.setNotRetractable(blobId);
        assert(!blobStore.getRetractable(blobId));
    }

    function testFailSetNotTransferableNotOwner() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        blobStoreProxy.setNotTransferable(blobId);
    }

    function testSetNotTransferable() {
        bytes20 blobId = blobStore.create(TRANSFERABLE, 0x1234, 0);
        assert(blobStore.getTransferable(blobId));
        blobStore.setNotTransferable(blobId);
        assert(!blobStore.getTransferable(blobId));
    }

}


Buy price check

Buy price check

pragma solidity ^0.4.10;
contract Registrar {
    function entries(bytes32 _hash) constant returns (uint, address, uint, uint, uint);
}

contract Deed {
    address public registrar;
    function getCreationDate() constant returns(uint);
    uint public creationDate;
    address public owner;
    address public previousOwner;
    uint public value;
    function Deed(address _owner);
    function setOwner(address newOwner);
    function setRegistrar(address newRegistrar);
    function setBalance(uint newValue, bool throwOnFailure);
    function closeDeed(uint refundRatio);
    function destroyDeed();
}

contract ENSTrade {
    struct Record {
        string name;
        bool listed;
        uint buyPrice;
        address nextRecord;
        address previousRecord;
        string message;
    }

    struct RecordOffers {
        address lastOffer;
        uint totalOfferCount;
        uint totalOfferValue;
    }

    struct Offer {
        address nextOffer;
        address previousOffer;
        uint value;
        string message;
    }

    event tradeComplete(address indexed deedAddress, address from, address to, uint value);
    event offerCreated(address indexed deedAddress, address from, uint value);
    event offerCancelled(address indexed deedAddress, address from);
    event listingCreated(address indexed deedAddress, address from, uint buyPrice);
    event listingRemoved(address indexed deedAddress, address from);
    event recordReclaimed(address indexed deedAddress, address to);

    address feeAddress;
    uint public fee = 0; // Out of 10000 (0% for alpha)
    uint public minimumOfferPrice = 0.01 ether;

    uint public recordsCurrentlyListed;
    uint public totalRecordsTraded;
    uint public totalValueTraded;

    address public registrarAddress;

    mapping (address => Record) records;
    mapping (address => mapping (address => Offer)) offers;
    mapping (address => RecordOffers) recordOffers;
    address public lastRecord;

    modifier onlyOwner(address _deedAddress) {
        // Check who owns the name using the previousOwner() function, as ens.trade should currently owns it.
        Deed d = Deed(_deedAddress);
        if (d.owner() != address(this)) throw;
        if (d.previousOwner() != msg.sender) throw;
        _;
    }

    modifier onlyFeeAddress {
        if (msg.sender != feeAddress) throw;
        _;
    }

    function ENSTrade(address _registrarAddress) {
        feeAddress = msg.sender;
        registrarAddress = _registrarAddress;
    }
    function setFeeAddress(address _feeAddress) onlyFeeAddress {
        feeAddress = _feeAddress;
    }
    function setMinimumOfferPrice(uint _minimumOfferPrice) onlyFeeAddress {
        minimumOfferPrice = _minimumOfferPrice;
    }
    function setFee(uint _fee) onlyFeeAddress {
        if (_fee > 500) throw; // Maximum 5%
        fee = _fee;
    }

    function sha(string _string) constant returns(bytes32) {
        return sha3(_string);
    }

    function newListing(string _name, uint _buyPrice, string _message) {
        // Hashes the name and checks that the sender previously owned it
        Registrar registrar = Registrar(registrarAddress);
        bytes32 hash = sha3(_name);
        var (,_deedAddress,,,) = registrar.entries(hash);
        Deed d = Deed(_deedAddress);
        if (d.owner() != address(this)) throw;
        if (d.previousOwner() != msg.sender) throw;
        if (_buyPrice == 0 || _buyPrice < minimumOfferPrice) throw; // For extra security

        Record r = records[_deedAddress];
        if (lastRecord != 0x0) {
            records[lastRecord].nextRecord = _deedAddress;
        }
        r.previousRecord = lastRecord;
        r.listed = true;
        r.name = _name;
        r.buyPrice = _buyPrice;
        r.message = _message;
        lastRecord = _deedAddress;
        recordsCurrentlyListed++;

        listingCreated(_deedAddress, msg.sender, _buyPrice);
    }

    function deList(address _deedAddress) onlyOwner(_deedAddress) {
        Record r = records[_deedAddress];
        if (r.listed) {
            deleteRecord(_deedAddress);
            listingRemoved(_deedAddress, msg.sender);
        }
    }

    function reclaim(address _deedAddress) onlyOwner(_deedAddress) {
        Record r = records[_deedAddress];
        Deed d = Deed(_deedAddress);
        d.setOwner(msg.sender);
        if (r.listed) {
            deleteRecord(_deedAddress);
            listingRemoved(_deedAddress, msg.sender);
        }
        recordReclaimed(_deedAddress, msg.sender);
    }

    function deleteRecord(address _deedAddress) internal {
        Record r = records[_deedAddress];
        records[r.previousRecord].nextRecord = r.nextRecord;
        if (_deedAddress == lastRecord) {
            lastRecord = r.previousRecord;
        }
        delete records[_deedAddress];
        recordsCurrentlyListed--;
    }

    function newOffer(address _deedAddress, string _message) payable {
        if (msg.value == 0) throw;
        Offer o = offers[_deedAddress][msg.sender];
        if (o.value > 0) throw; // Offer exists
        Record r = records[_deedAddress];
        if (msg.value >= r.buyPrice && r.buyPrice != 0) {
            // Offer is above asking price, finish trade
            transferRecord(_deedAddress, msg.sender, msg.value);
            return;
        }
        if (msg.value < minimumOfferPrice) throw;
        o.value = msg.value;
        o.message = _message;

        RecordOffers ro = recordOffers[_deedAddress];
        if (ro.lastOffer != 0x0) {
            offers[_deedAddress][ro.lastOffer].nextOffer = msg.sender;
        }
        o.previousOffer = ro.lastOffer;
        ro.lastOffer = msg.sender;
        ro.totalOfferCount++;
        ro.totalOfferValue += msg.value;

        offerCreated(_deedAddress, msg.sender, msg.value);
    }

    function cancelOffer(address _deedAddress) {
        Offer o = offers[_deedAddress][msg.sender];
        if (o.value == 0) throw;
        uint valueToSend = o.value;
        deleteOffer(_deedAddress, msg.sender);
        msg.sender.transfer(valueToSend);

        offerCancelled(_deedAddress, msg.sender);
    }

    function acceptOffer(address _deedAddress, address _offerAddress, uint _offerValue) onlyOwner(_deedAddress) {
        Offer o = offers[_deedAddress][_offerAddress];
        if (o.value != _offerValue || o.value == 0) throw; // For extra security and race conditions

        Record r = records[_deedAddress];
        if (!r.listed) throw;

        transferRecord(_deedAddress, _offerAddress, o.value);
        deleteOffer(_deedAddress, _offerAddress);
    }

    function deleteOffer(address _deedAddress, address _offerAddress) internal {
        RecordOffers ro = recordOffers[_deedAddress];
        Offer o = offers[_deedAddress][_offerAddress];
        offers[_deedAddress][o.previousOffer].nextOffer = o.nextOffer;
        if (ro.lastOffer == _offerAddress) {
            ro.lastOffer = o.previousOffer;
        }
        ro.totalOfferCount--;
        ro.totalOfferValue -= offers[_deedAddress][_offerAddress].value;
        delete offers[_deedAddress][_offerAddress];
    }

    function transferRecord(address _deedAddress, address _toAddress, uint _value) internal {
        uint _fee = _value * fee / 10000;
        Deed d = Deed(_deedAddress);
        totalRecordsTraded++;
        totalValueTraded += _value;

        d.setOwner(_toAddress);
        deleteRecord(_deedAddress);

        d.previousOwner().transfer(_value - _fee);
        feeAddress.transfer(_fee);

        tradeComplete(_deedAddress, d.previousOwner(), _toAddress, _value);
    }

    function getRecord(address _deedAddress) constant returns(bool, string, uint, address, address, string) {
        Record r = records[_deedAddress];
        return (r.listed, r.name, r.buyPrice, r.nextRecord, r.previousRecord, r.message);
    }

    function getRecordOffers(address _deedAddress) constant returns(address, uint, uint) {
        RecordOffers ro = recordOffers[_deedAddress];
        return (ro.lastOffer, ro.totalOfferCount, ro.totalOfferValue);
    }

    function getOffer(address _deedAddress, address _offerAddress) constant returns(address, address, uint, string) {
        Offer o = offers[_deedAddress][_offerAddress];
        return (o.nextOffer, o.previousOffer, o.value, o.message);
    }
}

pragma solidity ^0.4.10;
contract Registrar {
    function entries(bytes32 _hash) constant returns (uint, address, uint, uint, uint);
}

contract Deed {
    address public registrar;
    function getCreationDate() constant returns(uint);
    uint public creationDate;
    address public owner;
    address public previousOwner;
    uint public value;
    function Deed(address _owner);
    function setOwner(address newOwner);
    function setRegistrar(address newRegistrar);
    function setBalance(uint newValue, bool throwOnFailure);
    function closeDeed(uint refundRatio);
    function destroyDeed();
}

contract ENSTrade {
    struct Record {
        string name;
        bool listed;
        uint buyPrice;
        address nextRecord;
        address previousRecord;
        string message;
    }

    struct RecordOffers {
        address lastOffer;
        uint totalOfferCount;
        uint totalOfferValue;
    }

    struct Offer {
        address nextOffer;
        address previousOffer;
        uint value;
        string message;
    }

    event tradeComplete(address indexed deedAddress, address from, address to, uint value);
    event offerCreated(address indexed deedAddress, address from, uint value);
    event offerCancelled(address indexed deedAddress, address from);
    event listingCreated(address indexed deedAddress, address from, uint buyPrice);
    event listingRemoved(address indexed deedAddress, address from);
    event recordReclaimed(address indexed deedAddress, address to);

    address feeAddress;
    uint public fee = 0; // Out of 10000 (0% for alpha)
    uint public minimumOfferPrice = 0.01 ether;

    uint public recordsCurrentlyListed;
    uint public totalRecordsTraded;
    uint public totalValueTraded;

    address public registrarAddress;

    mapping (address => Record) records;
    mapping (address => mapping (address => Offer)) offers;
    mapping (address => RecordOffers) recordOffers;
    address public lastRecord;

    modifier onlyOwner(address _deedAddress) {
        // Check who owns the name using the previousOwner() function, as ens.trade should currently owns it.
        Deed d = Deed(_deedAddress);
        if (d.owner() != address(this)) throw;
        if (d.previousOwner() != msg.sender) throw;
        _;
    }

    modifier onlyFeeAddress {
        if (msg.sender != feeAddress) throw;
        _;
    }

    function ENSTrade(address _registrarAddress) {
        feeAddress = msg.sender;
        registrarAddress = _registrarAddress;
    }
    function setFeeAddress(address _feeAddress) onlyFeeAddress {
        feeAddress = _feeAddress;
    }
    function setMinimumOfferPrice(uint _minimumOfferPrice) onlyFeeAddress {
        minimumOfferPrice = _minimumOfferPrice;
    }
    function setFee(uint _fee) onlyFeeAddress {
        if (_fee > 500) throw; // Maximum 5%
        fee = _fee;
    }

    function sha(string _string) constant returns(bytes32) {
        return sha3(_string);
    }

    function newListing(string _name, uint _buyPrice, string _message) {
        // Hashes the name and checks that the sender previously owned it
        Registrar registrar = Registrar(registrarAddress);
        bytes32 hash = sha3(_name);
        var (,_deedAddress,,,) = registrar.entries(hash);
        Deed d = Deed(_deedAddress);
        if (d.owner() != address(this)) throw;
        if (d.previousOwner() != msg.sender) throw;

        Record r = records[_deedAddress];
        if (lastRecord != 0x0) {
            records[lastRecord].nextRecord = _deedAddress;
        }
        r.previousRecord = lastRecord;
        r.listed = true;
        r.name = _name;
        r.buyPrice = _buyPrice;
        r.message = _message;
        lastRecord = _deedAddress;
        recordsCurrentlyListed++;

        listingCreated(_deedAddress, msg.sender, _buyPrice);
    }

    function deList(address _deedAddress) onlyOwner(_deedAddress) {
        Record r = records[_deedAddress];
        if (r.listed) {
            deleteRecord(_deedAddress);
            listingRemoved(_deedAddress, msg.sender);
        }
    }

    function reclaim(address _deedAddress) onlyOwner(_deedAddress) {
        Record r = records[_deedAddress];
        Deed d = Deed(_deedAddress);
        d.setOwner(msg.sender);
        if (r.listed) {
            deleteRecord(_deedAddress);
            listingRemoved(_deedAddress, msg.sender);
        }
        recordReclaimed(_deedAddress, msg.sender);
    }

    function deleteRecord(address _deedAddress) internal {
        Record r = records[_deedAddress];
        records[r.previousRecord].nextRecord = r.nextRecord;
        if (_deedAddress == lastRecord) {
            lastRecord = r.previousRecord;
        }
        delete records[_deedAddress];
        recordsCurrentlyListed--;
    }

    function newOffer(address _deedAddress, string _message) payable {
        if (msg.value == 0) throw;
        Offer o = offers[_deedAddress][msg.sender];
        if (o.value > 0) throw; // Offer exists
        Record r = records[_deedAddress];
        if (msg.value >= r.buyPrice && r.buyPrice != 0) {
            // Offer is above asking price, finish trade
            transferRecord(_deedAddress, msg.sender, msg.value);
            return;
        }
        if (msg.value < minimumOfferPrice) throw;
        o.value = msg.value;
        o.message = _message;

        RecordOffers ro = recordOffers[_deedAddress];
        if (ro.lastOffer != 0x0) {
            offers[_deedAddress][ro.lastOffer].nextOffer = msg.sender;
        }
        o.previousOffer = ro.lastOffer;
        ro.lastOffer = msg.sender;
        ro.totalOfferCount++;
        ro.totalOfferValue += msg.value;

        offerCreated(_deedAddress, msg.sender, msg.value);
    }

    function cancelOffer(address _deedAddress) {
        Offer o = offers[_deedAddress][msg.sender];
        if (o.value == 0) throw;
        uint valueToSend = o.value;
        deleteOffer(_deedAddress, msg.sender);
        msg.sender.transfer(valueToSend);

        offerCancelled(_deedAddress, msg.sender);
    }

    function acceptOffer(address _deedAddress, address _offerAddress, uint _offerValue) onlyOwner(_deedAddress) {
        Offer o = offers[_deedAddress][_offerAddress];
        if (o.value != _offerValue || o.value == 0) throw; // For extra security and race conditions

        Record r = records[_deedAddress];
        if (!r.listed) throw;

        transferRecord(_deedAddress, _offerAddress, o.value);
        deleteOffer(_deedAddress, _offerAddress);
    }

    function deleteOffer(address _deedAddress, address _offerAddress) internal {
        RecordOffers ro = recordOffers[_deedAddress];
        Offer o = offers[_deedAddress][_offerAddress];
        offers[_deedAddress][o.previousOffer].nextOffer = o.nextOffer;
        if (ro.lastOffer == _offerAddress) {
            ro.lastOffer = o.previousOffer;
        }
        ro.totalOfferCount--;
        ro.totalOfferValue -= offers[_deedAddress][_offerAddress].value;
        delete offers[_deedAddress][_offerAddress];
    }

    function transferRecord(address _deedAddress, address _toAddress, uint _value) internal {
        uint _fee = _value * fee / 10000;
        Deed d = Deed(_deedAddress);
        totalRecordsTraded++;
        totalValueTraded += _value;

        d.setOwner(_toAddress);
        deleteRecord(_deedAddress);

        d.previousOwner().transfer(_value - _fee);
        feeAddress.transfer(_fee);

        tradeComplete(_deedAddress, d.previousOwner(), _toAddress, _value);
    }

    function getRecord(address _deedAddress) constant returns(bool, string, uint, address, address, string) {
        Record r = records[_deedAddress];
        return (r.listed, r.name, r.buyPrice, r.nextRecord, r.previousRecord, r.message);
    }

    function getRecordOffers(address _deedAddress) constant returns(address, uint, uint) {
        RecordOffers ro = recordOffers[_deedAddress];
        return (ro.lastOffer, ro.totalOfferCount, ro.totalOfferValue);
    }

    function getOffer(address _deedAddress, address _offerAddress) constant returns(address, address, uint, string) {
        Offer o = offers[_deedAddress][_offerAddress];
        return (o.nextOffer, o.previousOffer, o.value, o.message);
    }
}


Fixes broken link (#3728)

Fixes broken link (#3728)

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}