diff --git a/CryptoExchange.Net.Protobuf/Converters/Protobuf/ProtobufMessageAccessor.cs b/CryptoExchange.Net.Protobuf/Converters/Protobuf/ProtobufMessageAccessor.cs
new file mode 100644
index 0000000..d497428
--- /dev/null
+++ b/CryptoExchange.Net.Protobuf/Converters/Protobuf/ProtobufMessageAccessor.cs
@@ -0,0 +1,489 @@
+using CryptoExchange.Net.Converters.MessageParsing;
+using CryptoExchange.Net.Interfaces;
+using CryptoExchange.Net.Objects;
+using ProtoBuf;
+using ProtoBuf.Meta;
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.IO;
+using System.Runtime.InteropServices.ComTypes;
+using System.Text;
+using System.Text.Json;
+using System.Threading.Tasks;
+
+namespace CryptoExchange.Net.Converters.Protobuf
+{
+ ///
+ /// System.Text.Json message accessor
+ ///
+#if NET5_0_OR_GREATER
+ public abstract class ProtobufMessageAccessor<
+ [DynamicallyAccessedMembers(
+#if NET8_0_OR_GREATER
+ DynamicallyAccessedMemberTypes.NonPublicConstructors |
+ DynamicallyAccessedMemberTypes.PublicFields |
+ DynamicallyAccessedMemberTypes.NonPublicFields |
+ DynamicallyAccessedMemberTypes.NonPublicNestedTypes |
+ DynamicallyAccessedMemberTypes.PublicProperties |
+ DynamicallyAccessedMemberTypes.NonPublicProperties |
+#endif
+ DynamicallyAccessedMemberTypes.PublicNestedTypes |
+ DynamicallyAccessedMemberTypes.NonPublicMethods |
+ DynamicallyAccessedMemberTypes.PublicMethods
+ )]
+ TIntermediateType> : IMessageAccessor
+#else
+ public abstract class ProtobufMessageAccessor : IMessageAccessor
+#endif
+ {
+ ///
+ /// The intermediate deserialization object
+ ///
+ protected TIntermediateType? _intermediateType;
+
+ ///
+ public bool IsValid { get; set; }
+
+ ///
+ public abstract bool OriginalDataAvailable { get; }
+
+ ///
+ public object? Underlying => throw new NotImplementedException();
+
+ ///
+ /// ctor
+ ///
+ public ProtobufMessageAccessor()
+ {
+ }
+
+ ///
+ public NodeType? GetNodeType()
+ {
+ throw new Exception("");
+ }
+
+ ///
+ public NodeType? GetNodeType(MessagePath path)
+ {
+ if (_intermediateType == null)
+ throw new InvalidOperationException("Data not read");
+
+ object? value = _intermediateType;
+ foreach (var step in path)
+ {
+ if (value == null)
+ break;
+
+ if (step.Type == 0)
+ {
+ // array index
+ }
+ else if (step.Type == 1)
+ {
+ // property value
+ value = value.GetType().GetProperty(step.Property!)?.GetValue(value);
+ }
+ else
+ {
+ // property name
+ }
+ }
+
+ if (value == null)
+ return null;
+
+ var valueType = value.GetType();
+ if (valueType.IsArray)
+ return NodeType.Array;
+
+ if (IsSimple(valueType))
+ return NodeType.Value;
+
+ return NodeType.Object;
+ }
+
+ private static bool IsSimple(Type type)
+ {
+ if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
+ {
+ // nullable type, check if the nested type is simple.
+ return IsSimple(type.GetGenericArguments()[0]);
+ }
+ return type.IsPrimitive
+ || type.IsEnum
+ || type == typeof(string)
+ || type == typeof(decimal);
+ }
+
+
+ ///
+ public T? GetValue(MessagePath path)
+ {
+ if (_intermediateType == null)
+ throw new InvalidOperationException("Data not read");
+
+ object? value = _intermediateType;
+ foreach(var step in path)
+ {
+ if (value == null)
+ break;
+
+ if (step.Type == 0)
+ {
+ // array index
+ }
+ else if (step.Type == 1)
+ {
+ // property value
+ value = value.GetType().GetProperty(step.Property!)?.GetValue(value);
+ }
+ else
+ {
+ // property name
+ }
+ }
+
+ return (T?)value;
+ }
+
+ ///
+ public T?[]? GetValues(MessagePath path)
+ {
+ throw new Exception("");
+
+ }
+
+ ///
+ public abstract string GetOriginalString();
+
+ ///
+ public abstract void Clear();
+
+ ///
+ public abstract CallResult