contract TokenSwap is Ownable {

    /* neverdie token contract address and its instance, can be set by owner only */
    HumanStandardToken ndc;
    /* neverdie token contract address and its instance, can be set by owner only */
    HumanStandardToken tpt;
    /* signer address, verified in 'swap' method, can be set by owner only */
    address neverdieSigner;
    /* minimal amount for swap, the amount passed to 'swap method can't be smaller
       than this value, can be set by owner only */
    uint256 minSwapAmount = 40;

    event Swap(
        address indexed to,
        address indexed PTaddress,
        uint256 rate,
        uint256 amount,
        uint256 ptAmount
    );

    event BuyNDC(
        address indexed to,
        uint256 NDCprice,
        uint256 value,
        uint256 amount
    );

    event BuyTPT(
        address indexed to,
        uint256 TPTprice,
        uint256 value,
        uint256 amount
    );

    /// @dev handy constructor to initialize TokenSwap with a set of proper parameters
    /// NOTE: min swap amount is left with default value, set it manually if needed
    /// @param _teleportContractAddress Teleport token address 
    /// @param _neverdieContractAddress Neverdie token address
    /// @param _signer signer address, verified further in swap functions
    function TokenSwap(address _teleportContractAddress, address _neverdieContractAddress, address _signer) public {
        tpt = HumanStandardToken(_teleportContractAddress);
        ndc = HumanStandardToken(_neverdieContractAddress);
        neverdieSigner = _signer;
    }

    function setTeleportContractAddress(address _to) external onlyOwner {
        tpt = HumanStandardToken(_to);
    }

    function setNeverdieContractAddress(address _to) external onlyOwner {
        ndc = HumanStandardToken(_to);
    }

    function setNeverdieSignerAddress(address _to) external onlyOwner {
        neverdieSigner = _to;
    }

    function setMinSwapAmount(uint256 _amount) external onlyOwner {
        minSwapAmount = _amount;
    }

    /// @dev receiveApproval calls function encoded as extra data
    /// @param _sender token sender
    /// @param _value value allowed to be spent
    /// @param _tokenContract callee, should be equal to neverdieContractAddress
    /// @param _extraData  this should be a well formed calldata with function signature preceding which is used to call, for example, 'swap' method
    function receiveApproval(address _sender, uint256 _value, address _tokenContract, bytes _extraData) external {
        require(_tokenContract == address(ndc));
        assert(this.call(_extraData));
    }

    

    /// @dev One-way swapFor function, swaps NDC for purchasable token for a given spender
    /// @param _spender account that wants to swap NDC for purchasable token 
    /// @param _rate current NDC to purchasable token rate, i.e. that the returned amount 
    ///              of purchasable tokens equals to (_amount * _rate) / 1000
    /// @param _PTaddress the address of the purchasable token  
    /// @param _amount amount of NDC being offered
    /// @param _expiration expiration timestamp 
    /// @param _v ECDCA signature
    /// @param _r ECDSA signature
    /// @param _s ECDSA signature
    function swapFor(address _spender,
                     uint256 _rate,
                     address _PTaddress,
                     uint256 _amount,
                     uint256 _expiration,
                     uint8 _v,
                     bytes32 _r,
                     bytes32 _s) public {

        // Check if the signature did not expire yet by inspecting the timestamp
        require(_expiration >= block.timestamp);

        // Check if the signature is coming from the neverdie signer address
        address signer = ecrecover(keccak256(_spender, _rate, _PTaddress, _amount, _expiration), _v, _r, _s);
        require(signer == neverdieSigner);

        // Check if the amount of NDC is higher than the minimum amount 
        require(_amount >= minSwapAmount);
       
        // Check that we hold enough tokens
        HumanStandardToken ptoken = HumanStandardToken(_PTaddress);
        uint256 ptAmount;
        uint8 decimals = ptoken.decimals();
        if (decimals <= 18) {
          ptAmount = SafeMath.div(SafeMath.div(SafeMath.mul(_amount, _rate), 1000), 10**(uint256(18 - decimals)));
        } else {
          ptAmount = SafeMath.div(SafeMath.mul(SafeMath.mul(_amount, _rate), 10**(uint256(decimals - 18))), 1000);
        }

        assert(ndc.transferFrom(_spender, this, _amount) && ptoken.transfer(_spender, ptAmount));

        // Emit Swap event
        Swap(_spender, _PTaddress, _rate, _amount, ptAmount);
    }

    /// @dev One-way swap function, swaps NDC to purchasable tokens
    /// @param _rate current NDC to purchasable token rate, i.e. that the returned amount of purchasable tokens equals to _amount * _rate 
    /// @param _PTaddress the address of the purchasable token  
    /// @param _amount amount of NDC being offered
    /// @param _expiration expiration timestamp 
    /// @param _v ECDCA signature
    /// @param _r ECDSA signature
    /// @param _s ECDSA signature
    function swap(uint256 _rate,
                  address _PTaddress,
                  uint256 _amount,
                  uint256 _expiration,
                  uint8 _v,
                  bytes32 _r,
                  bytes32 _s) external {
        swapFor(msg.sender, _rate, _PTaddress, _amount, _expiration, _v, _r, _s);
    }

    /// @dev buy NDC with ether
    /// @param _NDCprice NDC price in Wei
    /// @param _expiration expiration timestamp
    /// @param _v ECDCA signature
    /// @param _r ECDSA signature
    /// @param _s ECDSA signature
    function buyNDC(uint256 _NDCprice,
                    uint256 _expiration,
                    uint8 _v,
                    bytes32 _r,
                    bytes32 _s
                   ) payable external {
        // Check if the signature did not expire yet by inspecting the timestamp
        require(_expiration >= block.timestamp);

        // Check if the signature is coming from the neverdie address
        address signer = ecrecover(keccak256(_NDCprice, _expiration), _v, _r, _s);
        require(signer == neverdieSigner);

        uint256 a = SafeMath.div(msg.value, _NDCprice);
        assert(ndc.transfer(msg.sender, a));

        // Emit BuyNDC event
        BuyNDC(msg.sender, _NDCprice, msg.value, a);
    }

    /// @dev buy TPT with ether
    /// @param _TPTprice TPT price in Wei
    /// @param _expiration expiration timestamp
    /// @param _v ECDCA signature
    /// @param _r ECDSA signature
    /// @param _s ECDSA signature
    function buyTPT(uint256 _TPTprice,
                    uint256 _expiration,
                    uint8 _v,
                    bytes32 _r,
                    bytes32 _s
                   ) payable external {
        // Check if the signature did not expire yet by inspecting the timestamp
        require(_expiration >= block.timestamp);

        // Check if the signature is coming from the neverdie address
        address signer = ecrecover(keccak256(_TPTprice, _expiration), _v, _r, _s);
        require(signer == neverdieSigner);

        uint256 a = SafeMath.div(msg.value, _TPTprice);
        assert(tpt.transfer(msg.sender, a));

        // Emit BuyNDC event
        BuyTPT(msg.sender, _TPTprice, msg.value, a);
    }

    /// @dev fallback function to reject any ether coming directly to the contract
    function () payable public { 
        revert(); 
    }

    /// @dev withdraw all ether
    function withdrawEther() external onlyOwner {
        owner.transfer(this.balance);
    }

    /// @dev withdraw token
    /// @param _tokenContract any kind of ERC20 token to withdraw from
    function withdraw(address _tokenContract) external onlyOwner {
        ERC20 token = ERC20(_tokenContract);
        uint256 balance = token.balanceOf(this);
        assert(token.transfer(owner, balance));
    }

    /// @dev kill contract, but before transfer all TPT, NDC tokens and ether to owner
    function kill() onlyOwner public {
        uint256 allNDC = ndc.balanceOf(this);
        uint256 allTPT = tpt.balanceOf(this);
        assert(ndc.transfer(owner, allNDC) && tpt.transfer(owner, allTPT));
        selfdestruct(owner);
    }

}
/// @title Token Swap Contract for Neverdie
/// @author Julia Altenried, Yuriy Kashnikov
function kill() onlyOwner public {
    uint256 allNDC = ndc.balanceOf(this);
    uint256 allTPT = tpt.balanceOf(this);
    assert(ndc.transfer(owner, allNDC) && tpt.transfer(owner, allTPT));
    selfdestruct(owner);
}

/// @dev kill contract, but before transfer all TPT, NDC tokens and ether to owner