contract InstantRefundRelay is RelayerManager {
    using SafeMath for uint256;

    event RelayExecuted(bytes32 indexed relayTxId, bool success, address indexed to, uint gasUsed, uint gasPrice);
    event OutOfCoins();

    // @dev The relay transaction
    struct RelayTx {
        bytes32 id; // Off-chain identifier to track transaction & payment.
        address to; // Address for external contract.
        bytes data; // Call data that we need to send. Includes function call name, etc.
        uint gasLimit; // How much gas is allocated to this function call?
    }
    
    /** 
     * @param _relayTx A relay tx containing the job to execute
     * @param _gasRefund Gas amount to refund
     * @dev Only authorised relayer can execute relay jobs and they are refunded gas at the end of the call.
     *     Critically, if the relay job fails, we can simply catch exception and continue with the refund. 
     */
    function execute(RelayTx calldata _relayTx, uint _gasRefund) external {
        uint gasStarted = gasleft();

        // The msg.sender check protects against two problems:
        // - Replay attacks across chains (chainid in transaction)
        // - Re-entrancy attacks back into .execute() (signer required)
        // - Stops external relayers spending the contract balance without any.sender authorisation
        require(relayers[msg.sender], "Relayer must call this function.");

        // In the worst case, the contract will only send 63/64 of the transaction's
        // remaining gas due to https://eips.ethereum.org/EIPS/eip-150
        // But this is problematic as outlined in https://eips.ethereum.org/EIPS/eip-1930
        // so to fix... we need to make sure we supply 64/63 * gasLimit.
        // Assumption: Underlying contract called did not have a minimum gas required check
        // We add 1000 to cover the cost of calculating new gas limit - this should be a lot more than
        // is required - measuring shows cost of 58
        require(gasleft() > _relayTx.gasLimit.div(63).add(_relayTx.gasLimit).add(1000), "Not enough gas supplied.");

        // execute the actual call
        (bool success,) = _relayTx.to.call.gas(_relayTx.gasLimit)(_relayTx.data);

        uint gasUsed = gasStarted.add(_gasRefund).sub(gasleft()); // Takes into account gas cost for refund. 

        if(_gasRefund > 0) {
            if(!msg.sender.send(gasUsed*tx.gasprice)) {
                // Notify admin we need to provide more refund to this contract
                emit OutOfCoins();
            }
        }

        emit RelayExecuted(_relayTx.id, success, _relayTx.to, gasUsed, tx.gasprice);
    }
}
/** 
 * @author Patrick McCorry & Chris Buckland (ITX)
 * @title Relay contract
 * @notice Handles relaying transactions, managing the relayers & user deposits. 
 * @dev Two-stage deployment required. Deploy via CREATE2 and INIT() in the same transaction.
 */
function execute(RelayTx calldata _relayTx, uint _gasRefund) external {
    uint gasStarted = gasleft();

    // The msg.sender check protects against two problems:
    // - Replay attacks across chains (chainid in transaction)
    // - Re-entrancy attacks back into .execute() (signer required)
    // - Stops external relayers spending the contract balance without any.sender authorisation
    require(relayers[msg.sender], "Relayer must call this function.");

    // In the worst case, the contract will only send 63/64 of the transaction's
    // remaining gas due to https://eips.ethereum.org/EIPS/eip-150
    // But this is problematic as outlined in https://eips.ethereum.org/EIPS/eip-1930
    // so to fix... we need to make sure we supply 64/63 * gasLimit.
    // Assumption: Underlying contract called did not have a minimum gas required check
    // We add 1000 to cover the cost of calculating new gas limit - this should be a lot more than
    // is required - measuring shows cost of 58
    require(gasleft() > _relayTx.gasLimit.div(63).add(_relayTx.gasLimit).add(1000), "Not enough gas supplied.");

    // execute the actual call
    (bool success,) = _relayTx.to.call.gas(_relayTx.gasLimit)(_relayTx.data);

    uint gasUsed = gasStarted.add(_gasRefund).sub(gasleft()); // Takes into account gas cost for refund. 

    if(_gasRefund > 0) {
        if(!msg.sender.send(gasUsed*tx.gasprice)) {
            // Notify admin we need to provide more refund to this contract
            emit OutOfCoins();
        }
    }

    emit RelayExecuted(_relayTx.id, success, _relayTx.to, gasUsed, tx.gasprice);
}

/** 
 * @param _relayTx A relay tx containing the job to execute
 * @param _gasRefund Gas amount to refund
 * @dev Only authorised relayer can execute relay jobs and they are refunded gas at the end of the call.
 *     Critically, if the relay job fails, we can simply catch exception and continue with the refund. 
 */