pragma solidity ^0.5.7;

interface MarmoStork {
    function reveal(address _signer) external payable;
    function marmoOf(address _signer) external view;
    function hash() external view returns (bytes32);
}

interface Marmo {
    function relayedBy(bytes32 _id) external view returns (address _relayer);
    function relay(
        address _implementation,
        bytes calldata _data,
        bytes calldata _signature
    ) external;
}

contract MarmoRelayerHelper {
    bytes1 private constant CREATE2_PREFIX = byte(0xff);
    bytes32 private hash;

    MarmoStork public stork;

    constructor(MarmoStork _stork) public {
        hash = _stork.hash();
        stork = _stork;
    }

    // Calculates the Marmo wallet for a given signer
    // the wallet contract will be deployed in a deterministic manner
    function _marmoOf(address _signer) internal view returns (address) {
        // CREATE2 address
        return address(
            uint256(
                keccak256(
                    abi.encodePacked(
                        CREATE2_PREFIX,
                        stork,
                        bytes32(uint256(_signer)),
                        hash
                    )
                )
            )
        );
    }
    
    function _isNotContract(address _address) internal view returns (bool v) {
        assembly {
            v := iszero(extcodesize(_address))
        }
    }
    
    function wasRelayed(
        address _signer,
        bytes32 _id
    ) external view returns (bool) {
        Marmo marmo = Marmo(_marmoOf(_signer));

        if (_isNotContract(address(marmo))) {
            return false;
        }
        
        return marmo.relayedBy(_id) != address(0);
    }
    
    function depsReady(
        bytes calldata _data
    ) external view returns (bool) {
        // Retrieve inputs from data
        (bytes memory dependency) = abi.decode(_data, (bytes));
        return _checkDependency(dependency);
    }
    
    function revealAndRelay(
        address _signer,
        address _implementation,
        bytes calldata _data,
        bytes calldata _signature
    ) external {
        Marmo marmo = Marmo(_marmoOf(_signer));

        if (_isNotContract(address(marmo))) {
            stork.reveal(_signer);
        }
        
        marmo.relay(
            _implementation,
            _data,
            _signature
        );
    }
    
    // internal
    
    // The dependency is a 'staticcall' to a 'target'
    //  when the call succeeds and it does not return false, the dependency is satisfied.
    // [160 bits (target) + n bits (data)]
    function _checkDependency(bytes memory _dependency) internal view returns (bool result) {
        if (_dependency.length == 0) {
            result = true;
        } else {
            assembly {
                let response := mload(0x40)
                let success := staticcall(
                    gas,
                    mload(add(_dependency, 20)),
                    add(52, _dependency),
                    sub(mload(_dependency), 20),
                    response,
                    32
                )

                result := and(gt(success, 0), gt(mload(response), 0))
            }
        }
    }
}