All Downloads are FREE. Search and download functionalities are using the official Maven repository.

info.bitrich.xchangestream.serum.SerumStreamingService Maven / Gradle / Ivy

The newest version!
package info.bitrich.xchangestream.serum;

import static info.bitrich.xchangestream.serum.SerumEventType.subscribe;
import static info.bitrich.xchangestream.serum.SerumEventType.unknown;
import static info.bitrich.xchangestream.serum.SerumEventType.unsubscribe;

import com.fasterxml.jackson.databind.JsonNode;
import com.knowm.xchange.serum.SerumAdapters;
import com.knowm.xchange.serum.SerumConfigs.Commitment;
import com.knowm.xchange.serum.SerumConfigs.SubscriptionType;
import info.bitrich.xchangestream.serum.dto.SerumWsSubscriptionMessage;
import info.bitrich.xchangestream.service.netty.JsonNettyStreamingService;
import java.io.IOException;
import java.time.Duration;
import org.knowm.xchange.currency.CurrencyPair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SerumStreamingService extends JsonNettyStreamingService {

  private static final Logger LOG = LoggerFactory.getLogger(SerumStreamingService.class);

  private final SerumSubscriptionManager subscriptionManager = new SerumSubscriptionManager();

  private final String RESULT = "result";
  private final String SUBSCRIPTION = "subscription";
  private final String PARAMS = "params";
  private final String ID = "id";

  public SerumStreamingService(String apiUrl) {
    super(apiUrl);
  }

  public SerumStreamingService(
      String apiUrl,
      int maxFramePayloadLength,
      Duration connectionTimeout,
      Duration retryDuration,
      int idleTimeoutSeconds) {
    super(apiUrl, maxFramePayloadLength, connectionTimeout, retryDuration, idleTimeoutSeconds);
  }

  /**
   * Channel name is mapped from the subscription id
   *
   * @param message to extract it from
   */
  @Override
  protected String getChannelNameFromMessage(JsonNode message) {
    if (message.has(PARAMS) && message.get(PARAMS).has(SUBSCRIPTION)) {
      final int subID = message.get(PARAMS).get(SUBSCRIPTION).intValue();
      return subscriptionManager.getChannelName(subID);
    }
    return null;
  }

  /**
   * Infers message event type based on if a "result" field is present and if it's a boolean or an
   * int.
   *
   * 

Boolean indicates that it was an unsubscribe as it determines if it was successfully * unsubscribed. * *

Int tells us it's a subscribe as it's the subscription id being used for that channel. * *

https://docs.solana.com/developing/clients/jsonrpc-api#subscription-websocket * * @param message to infer from * @return event type */ private SerumEventType getMessageEvent(final JsonNode message) { if (message.has(RESULT)) { if (message.get(RESULT).isBoolean()) { return unsubscribe; } if (message.get(RESULT).isInt()) { return subscribe; } } return unknown; } @Override protected void handleMessage(final JsonNode message) { try { if (!message.has(ID)) { super.handleMessage(message); return; } final int reqID = message.get(ID).intValue(); switch (getMessageEvent(message)) { case subscribe: final int subID = message.get(RESULT).intValue(); subscriptionManager.newSubscription(reqID, subID); break; case unsubscribe: boolean status = message.get(RESULT).asBoolean(); subscriptionManager.removedSubscription(reqID, status); break; case unknown: default: LOG.error("Unknown message type on msg {}", message); } } catch (Exception e) { LOG.error("Issue processing message {}", message, e); } } @Override public String getSubscribeMessage(String channelName, Object... args) throws IOException { if (args.length < 3) { throw new IllegalArgumentException("Not enough args"); } if (!(args[0] instanceof CurrencyPair)) { throw new IllegalArgumentException("arg[0] must be the currency pairs"); } if (!(args[1] instanceof SubscriptionType)) { throw new IllegalArgumentException("arg[1] must be the subscription type"); } if (!(args[2] instanceof String)) { throw new IllegalArgumentException("arg[1] must be the market data type"); } final SubscriptionType subscriptionType = (SubscriptionType) args[1]; final String account = SerumAdapters.getSolanaDataTypeAddress((CurrencyPair) args[0], (String) args[2]); final Commitment commitment = args.length > 3 && args[2] != null && args[2] instanceof Commitment ? (Commitment) args[2] : Commitment.max; int reqID = subscriptionManager.generateNewInflightRequest(channelName); return new SerumWsSubscriptionMessage(commitment, subscriptionType, account, reqID).buildMsg(); } @Override public String getUnsubscribeMessage(String channelName, Object... args) throws IOException { return null; } public String buildChannelName( final CurrencyPair pair, final SubscriptionType subscriptionType, final Object... args) { switch (subscriptionType) { case accountSubscribe: if (args == null || args.length < 1 || !(args[0] instanceof String)) { throw new IllegalArgumentException( String.format("No/incorrect market data type for %s specified", pair.toString())); } final String marketDataType = (String) args[0]; return subscriptionType.name() + "_" + SerumAdapters.getSolanaDataTypeAddress(pair, marketDataType); } throw new UnsupportedOperationException( String.format("Unsupported subscription type %s", subscriptionType)); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy