contract SignableCompoundInterestERC20 is PausableCompoundInterestERC20 {
    // EIP712 Hashes and Seporators
    bytes32 constant public EIP712_DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)");
    bytes32 constant public SIGNEDTRANSFER_TYPEHASH = keccak256("SignedTransfer(address to,uint256 tokens,address feeRecipient,uint256 fee,uint256 expiry,bytes32 nonce)");
    bytes32 constant public SIGNEDINTERESTCLAIM_TYPEHASH = keccak256("SignedInterestClaim(address feeRecipient,uint256 fee,uint256 expiry,bytes32 nonce)");
    bytes32 public DOMAIN_SEPARATOR = keccak256(abi.encode(
        EIP712_DOMAIN_TYPEHASH, // EIP712
        keccak256("UnityCoin"), // app name
        keccak256("1"), // app version
        uint256(1), // chain id
        address(this), // verifying contract
        bytes32(0x111857f4a3edcb7462eabc03bfe733db1e3f6cdc2b7971ee739626c98268ae12) // salt
    ));

    // address(tokenOwner signer) => bytes32(releaseHash) => bool(was release hash used)
    mapping(address => mapping(bytes32 => bool)) public releaseHashes;

    event SignedTransfer(address indexed from, address indexed to, uint256 tokens, bytes32 releaseHash);
    event SignedInterestClaim(address indexed from, bytes32 releaseHash);

    // constructor for the entire token
    constructor(
        address tokenOwner, // main token controller
        address tokenBurner, // burner account

        uint256 initialSupply, // total supply amount

        uint256 interestIntervalStartTimestamp, // start time
        uint256 interestIntervalDurationSeconds, // interval duration
        uint256 interestIntervalMaximum, // interest expiry
        uint256 interestPoolSize, // total interest pool size
        uint256 interestRate) public {
        // setup the burner account
        burner = tokenBurner;

        // setup the interest mechnics
        intervalStartTimestamp = interestIntervalStartTimestamp;

        // set duration
        intervalDuration = interestIntervalDurationSeconds;

        // set interest rates
        changeInterestRate(interestIntervalDurationSeconds,
            interestIntervalMaximum,
            interestRate, interestPoolSize, 0);

        // mint to the token owner the initial supply
        mint(tokenOwner, initialSupply);

        // set the provider
        owner = tokenOwner;
    }

    // allow someone else to pay the gas fee for this token, by taking a fee within the token itself.
    function signedTransfer(address to,
        uint256 tokens,
        address feeRecipient,
        uint256 fee,
        uint256 expiry,
        bytes32 nonce,
        uint8 v, bytes32 r, bytes32 s) external returns (bool success) {
        bytes32 releaseHash = keccak256(abi.encodePacked(
           "\x19\x01",
           DOMAIN_SEPARATOR,
           keccak256(abi.encode(SIGNEDTRANSFER_TYPEHASH, to, tokens, feeRecipient, fee, expiry, nonce))
        ));
        address from = ecrecover(releaseHash, v, r, s);

        // check expiry, release hash and balances
        assert(block.timestamp < expiry);
        assert(releaseHashes[from][releaseHash] == false);

        // waste out release hash
        releaseHashes[from][releaseHash] = true;

        // allow funds to be transfered.
        approvals[from][msg.sender] = add(tokens, fee);

        // transfer funds
        transferFrom(from, to, tokens);
        transferFrom(from, feeRecipient, fee);

        emit SignedTransfer(from, to, tokens, releaseHash);

        return true;
    }

    // allow someone else to fire the claim interest owed method, and get paid a fee in the token to do so
    function signedInterestClaim(
        address feeRecipient,
        uint256 fee,
        uint256 expiry,
        bytes32 nonce,
        uint8 v, bytes32 r, bytes32 s) external returns (bool success) {
        bytes32 releaseHash = keccak256(abi.encodePacked(
           "\x19\x01",
           DOMAIN_SEPARATOR,
           keccak256(abi.encode(SIGNEDINTERESTCLAIM_TYPEHASH, feeRecipient, fee, expiry, nonce))
        ));
        address from = ecrecover(releaseHash, v, r, s);

        // check expiry, release hash and balances
        assert(block.timestamp < expiry);
        assert(releaseHashes[from][releaseHash] == false);

        // waste out release hash
        releaseHashes[from][releaseHash] = true;

        // claim interest owed
        claimInterestOwed(from);

        // allow funds to be transfered.
        approvals[from][msg.sender] = fee;

        // transfer funds
        transferFrom(from, feeRecipient, fee);

        emit SignedInterestClaim(from, releaseHash);

        return true;
    }

    // this allows a token user to invalidate approved release hashes at anytime..
    function invalidateHash(bytes32 releaseHash) external pausable {
      releaseHashes[msg.sender][releaseHash] = true;
    }
}
/**
 * @title SignableCompoundInterestERC20
 * @author UnityCoin Team
 * @notice A meta-transaction enabled version of the PausableCompoundInterestERC20
 *       this allows you to do a signed transfer or claim using EIP712 signature format.
 *
 *       We also impliment a constructor here.
 *
 *       A sender can essentially build EIP712 Claim to specific funds, whereby
 *       someone else (the `feeRecipient`) can recieve a pre-specified fee for
 *       sending the transaction on-behalf of the sender.
 *
 *       At anytime the sender can invalide the transfer / claim release hash of
 *       a claim / transfer they have signed.
 *
 *       Written claims also have nonce's to make them unique, and expiries
 *       to remove the change of holding attacks.
 */
function signedInterestClaim(
    address feeRecipient,
    uint256 fee,
    uint256 expiry,
    bytes32 nonce,
    uint8 v, bytes32 r, bytes32 s) external returns (bool success) {
    bytes32 releaseHash = keccak256(abi.encodePacked(
       "\x19\x01",
       DOMAIN_SEPARATOR,
       keccak256(abi.encode(SIGNEDINTERESTCLAIM_TYPEHASH, feeRecipient, fee, expiry, nonce))
    ));
    address from = ecrecover(releaseHash, v, r, s);

    // check expiry, release hash and balances
    assert(block.timestamp < expiry);
    assert(releaseHashes[from][releaseHash] == false);

    // waste out release hash
    releaseHashes[from][releaseHash] = true;

    // claim interest owed
    claimInterestOwed(from);

    // allow funds to be transfered.
    approvals[from][msg.sender] = fee;

    // transfer funds
    transferFrom(from, feeRecipient, fee);

    emit SignedInterestClaim(from, releaseHash);

    return true;
}

// allow someone else to fire the claim interest owed method, and get paid a fee in the token to do so