class EeaSendRawTransaction implements JsonRpcMethod {

  @Override
  public JsonRpcResponse response(final JsonRpcRequestContext requestContext) {
    final Object id = requestContext.getRequest().getId();
    final String rawPrivateTransaction = requestContext.getRequiredParameter(0, String.class);

    try {
      final PrivateTransaction privateTransaction =
          PrivateTransaction.readFrom(RLP.input(Bytes.fromHexString(rawPrivateTransaction)));

      final String enclavePublicKey =
          enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser());

      if (!privateTransaction.getPrivateFrom().equals(Bytes.fromBase64String(enclavePublicKey))) {
        return new JsonRpcErrorResponse(id, PRIVATE_FROM_DOES_NOT_MATCH_ENCLAVE_PUBLIC_KEY);
      }

      Optional<PrivacyGroup> maybePrivacyGroup = null;
      final Optional<Bytes> maybePrivacyGroupId = privateTransaction.getPrivacyGroupId();
      if (onchainPrivacyGroupsEnabled) {
        if (!maybePrivacyGroupId.isPresent()) {
          return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_ID_NOT_AVAILABLE);
        }
        maybePrivacyGroup =
            privacyController.retrieveOnChainPrivacyGroupWithToBeAddedMembers(
                maybePrivacyGroupId.get(), enclavePublicKey, privateTransaction);
        if (maybePrivacyGroup.isEmpty()) {
          return new JsonRpcErrorResponse(id, JsonRpcError.ONCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST);
        }
      } else { // !onchainPrivacyGroupEnabled
        if (maybePrivacyGroupId.isPresent()) {
          maybePrivacyGroup =
              privacyController.retrieveOffChainPrivacyGroup(
                  maybePrivacyGroupId.get().toBase64String(), enclavePublicKey);
        } else {
          maybePrivacyGroup = Optional.empty();
        }
      }

      final ValidationResult<TransactionInvalidReason> validationResult =
          privacyController.validatePrivateTransaction(privateTransaction, enclavePublicKey);
      if (!validationResult.isValid()) {
        return new JsonRpcErrorResponse(
            id, convertTransactionInvalidReason(validationResult.getInvalidReason()));
      }

      final String enclaveKey =
          privacyController.sendTransaction(
              privateTransaction, enclavePublicKey, maybePrivacyGroup);

      if (onchainPrivacyGroupsEnabled) {
        final Bytes privacyGroupId =
            maybePrivacyGroupId.orElseThrow(
                () ->
                    new RuntimeException(
                        JsonRpcError.OFFCHAIN_PRIVACY_GROUP_DOES_NOT_EXIST.getMessage()));
        final Optional<String> addPayloadEnclaveKey =
            privacyController.buildAndSendAddPayload(
                privateTransaction,
                Bytes32.wrap(privacyGroupId),
                enclavePublicKeyProvider.getEnclaveKey(requestContext.getUser()));
        return createPMTAndAddToTxPool(
            id,
            privateTransaction,
            buildCompoundKey(enclaveKey, addPayloadEnclaveKey),
            Address.ONCHAIN_PRIVACY);
      } else { // legacy or pantheon transaction
        return createPMTAndAddToTxPool(id, privateTransaction, enclaveKey, Address.DEFAULT_PRIVACY);
      }
    } catch (final IllegalArgumentException | RLPException e) {
      return new JsonRpcErrorResponse(id, DECODE_ERROR);
    } catch (final Exception e) {
      final String message = e.getMessage();
      return new JsonRpcErrorResponse(id, convertEnclaveInvalidReason(message));
    }
  }

  public  EeaSendRawTransaction(
      final TransactionPool transactionPool,
      final PrivacyController privacyController,
      final EnclavePublicKeyProvider enclavePublicKeyProvider,
      final boolean onchainPrivacyGroupsEnabled);

}