// File: /D/projects/beam/eth_swap_contracts/contracts/SwapContractAggregateSignature.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.7.0;

import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol";

contract SwapContractAggregateSignature {

    struct Swap {
        uint refundTimeInBlocks;
        address initiator;
        address participant;
        uint256 value;
    }

    mapping(address => Swap) swaps;

    function initiate(uint refundTimeInBlocks, address addressFromSecret, address participant) public
        payable
    {
        require(swaps[addressFromSecret].refundTimeInBlocks == 0, "swap for this hash is already initiated");
        require(participant != address(0), "invalid participant address");
        require(block.number < refundTimeInBlocks, "refundTimeInBlocks has already come");

        swaps[addressFromSecret].refundTimeInBlocks = refundTimeInBlocks;
        swaps[addressFromSecret].participant = participant;
        swaps[addressFromSecret].initiator = msg.sender;
        swaps[addressFromSecret].value = msg.value;
    }

    function redeem(address addressFromSecret, bytes32 r, bytes32 s, uint8 v) public
    {
        require(msg.sender == swaps[addressFromSecret].participant, "invalid msg.sender");

        bytes32 hash = keccak256(abi.encodePacked(addressFromSecret, swaps[addressFromSecret].participant, swaps[addressFromSecret].initiator, swaps[addressFromSecret].refundTimeInBlocks));

        // If the signature is valid (and not malleable), return the signer address
        bytes32 ethSignedMessageHash = ECDSA.toEthSignedMessageHash(hash);
        address signer = ECDSA.recover(ethSignedMessageHash,  abi.encodePacked(r, s, v));

        require(signer == addressFromSecret, "invalid address");

        Swap memory tmp = swaps[addressFromSecret];
        delete swaps[addressFromSecret];

        (bool success, ) = payable(tmp.participant).call{value: tmp.value}("");
        require(success, "Transfer failed.");
    }

    function refund(address addressFromSecret) public
    {
        require(block.number >= swaps[addressFromSecret].refundTimeInBlocks, "refundTimeInBlocks has not come");
        require(msg.sender == swaps[addressFromSecret].initiator, "invalid msg.sender");

        Swap memory tmp = swaps[addressFromSecret];
        delete swaps[addressFromSecret];

        (bool success, ) = payable(tmp.initiator).call{value: tmp.value}("");
        require(success, "Transfer failed.");
    }

    function getSwapDetails(address addressFromSecret)
    public view returns (uint refundTimeInBlocks, address initiator, address participant, uint256 value)
    {
        refundTimeInBlocks = swaps[addressFromSecret].refundTimeInBlocks;
        initiator = swaps[addressFromSecret].initiator;
        participant = swaps[addressFromSecret].participant;
        value = swaps[addressFromSecret].value;
    }
}
// File: openzeppelin-solidity/contracts/cryptography/ECDSA.sol



// SPDX-License-Identifier: MIT

pragma solidity >=0.6.0 <0.8.0;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        // Check the signature length
        if (signature.length != 65) {
            revert("ECDSA: invalid signature length");
        }

        // Divide the signature in r, s and v variables
        bytes32 r;
        bytes32 s;
        uint8 v;

        // ecrecover takes the signature parameters, and the only way to get them
        // currently is to use assembly.
        // solhint-disable-next-line no-inline-assembly
        assembly {
            r := mload(add(signature, 0x20))
            s := mload(add(signature, 0x40))
            v := byte(0, mload(add(signature, 0x60)))
        }

        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value");
        require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value");

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        require(signer != address(0), "ECDSA: invalid signature");

        return signer;
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * replicates the behavior of the
     * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
     * JSON-RPC method.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }
}