// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol";

contract MultiSigWallet {
    using ECDSA for bytes32;

    address[2] public owners;

    constructor(address[2] memory _owners) payable {
        owners = _owners;
    }

    function deposit() external payable {}

    // transfer 함수는 **소유자의 동일한 서명을 이용해 여러번 실행이 가능**하다.
    function transfer(address _to, uint _amount, bytes[2] memory _sigs) external {
        bytes32 txHash = getTxHash(_to, _amount);
        require(_checkSigs(_sigs, txHash), "Invalid Sig");

        (bool sent, ) = _to.call{value: _amount}("");
        require(sent, "Failed");
    }

    function getTxHash(address _to, uint _amount) public view returns (bytes32) {
        return keccak256(abi.encodePacked(_to, _amount));
    }

    function _checkSigs(bytes[2] memory _sigs, bytes32 _txHash) private view returns (bool) {
        bytes32 ethSignedHash = _txHash.toEthSignedMessageHash();

        for(uint i = 0 ; i < _sigs.length ; i++ ){
            address signer = ethSignedHash.recover(_sigs[i]);
            bool valid = signer == owners[i];

            if(!valid) {
                return false;
            }
        }
        return true;
    }
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v4.5/contracts/utils/cryptography/ECDSA.sol";

contract MultiSigWallet {
    using ECDSA for bytes32;

    address[2] public owners;
    mapping(bytes32 => bool) public executed;

    constructor(address[2] memory _owners) payable {
        owners = _owners;
    }

    function deposit() external payable {}

    // **transfer 함수에 nonce 값을 추가**한다.
    function transfer(address _to, uint _amount, **uint _nonce**, bytes[2] memory _sigs) external {
        bytes32 txHash = getTxHash(_to, _amount, **_nonce**);
        require(!executed[txHash]);
        require(_checkSigs(_sigs, txHash), "Invalid Sig");

        executed[txHash] = true;

        (bool sent, ) = _to.call{value: _amount}("");
        require(sent, "Failed");
    }

    // **txHash 값에 address(this) 와 nonce 를 추가**한다.
    function getTxHash(address _to, uint _amount, **uint _nonce**) public view returns (bytes32) {
        return keccak256(abi.encodePacked(**address(this)**, _to, _amount, **_nonce**));
    }

    function _checkSigs(bytes[2] memory _sigs, bytes32 _txHash) private view returns (bool) {
        bytes32 ethSignedHash = _txHash.toEthSignedMessageHash();

        for(uint i = 0 ; i < _sigs.length ; i++ ){
            address signer = ethSignedHash.recover(_sigs[i]);
            bool valid = signer == owners[i];

            if(!valid) {
                return false;
            }
        }
        return true;
    }
}