Skip to content

Commit f519435

Browse files
feat: add missing RPC queries + 6 event parsers for full JS parity
RpcClient.cs: - QuerySessionsForAccountAsync — sessions via RPC - QuerySubscriptionsForAccountAsync — subscriptions via RPC - QueryPlanAsync — single plan by ID via RPC EventParser.cs — 6 new typed event parsers: - ParseSessionRefund (sentinel.node.v3.EventRefund) - ParseNodeStatus (sentinel.node.v3.EventUpdateStatus) - ParseSessionDetails (sentinel.session.v3.EventUpdateDetails) - ParseSubscriptionPay (sentinel.subscription.v3.EventPay) - ParseSubscriptionEnd (sentinel.subscription.v3.EventEnd) - ParseLeaseEnd (sentinel.lease.v1.EventEnd) Now 12/12 event types have typed parsers (was 6/12). Now 8 RPC query methods (was 5). Full parity with JS SDK. Build: 0 errors.
1 parent 9b8ea93 commit f519435

2 files changed

Lines changed: 151 additions & 0 deletions

File tree

src/Sentinel.SDK.Core/EventParser.cs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,26 @@ public record LeaseCreatedEvent(
7878
int MaxHours
7979
);
8080

81+
/// <summary>Parsed sentinel.node.v3.EventRefund.</summary>
82+
public record SessionRefundEvent(long SessionId, string AccAddress, string Amount);
83+
84+
/// <summary>Parsed sentinel.node.v3.EventUpdateStatus.</summary>
85+
public record NodeStatusEvent(string Address, int Status);
86+
87+
/// <summary>Parsed sentinel.session.v3.EventUpdateDetails (bandwidth report).</summary>
88+
public record SessionDetailsEvent(long SessionId, string AccAddress, string NodeAddress,
89+
string DownloadBytes, string UploadBytes, string Duration);
90+
91+
/// <summary>Parsed sentinel.subscription.v3.EventPay.</summary>
92+
public record SubscriptionPayEvent(long SubscriptionId, long PlanId, string AccAddress,
93+
string ProvAddress, string Payment, string StakingReward);
94+
95+
/// <summary>Parsed sentinel.subscription.v3.EventEnd.</summary>
96+
public record SubscriptionEndEvent(long SubscriptionId, long PlanId, string AccAddress);
97+
98+
/// <summary>Parsed sentinel.lease.v1.EventEnd.</summary>
99+
public record LeaseEndEvent(long LeaseId, string NodeAddress, string ProvAddress);
100+
81101
// ─── Parsing Methods ───────────────────────────────────────────
82102

83103
/// <summary>
@@ -198,6 +218,83 @@ int MaxHours
198218
);
199219
}
200220

221+
/// <summary>Parse a SessionRefundEvent from TX result events.</summary>
222+
public static SessionRefundEvent? ParseSessionRefund(string? txEventsJson)
223+
{
224+
var attrs = FindEvent(txEventsJson, EventTypes.NodeRefund);
225+
if (attrs is null) return null;
226+
return new SessionRefundEvent(
227+
SessionId: ParseLong(attrs.GetValueOrDefault("session_id")),
228+
AccAddress: attrs.GetValueOrDefault("acc_address") ?? "",
229+
Amount: attrs.GetValueOrDefault("amount") ?? attrs.GetValueOrDefault("value") ?? "0"
230+
);
231+
}
232+
233+
/// <summary>Parse a NodeStatusEvent from TX result events.</summary>
234+
public static NodeStatusEvent? ParseNodeStatus(string? txEventsJson)
235+
{
236+
var attrs = FindEvent(txEventsJson, EventTypes.NodeUpdateStatus);
237+
if (attrs is null) return null;
238+
return new NodeStatusEvent(
239+
Address: attrs.GetValueOrDefault("address") ?? "",
240+
Status: int.TryParse(attrs.GetValueOrDefault("status"), out var s) ? s : 0
241+
);
242+
}
243+
244+
/// <summary>Parse a SessionDetailsEvent (bandwidth update) from TX result events.</summary>
245+
public static SessionDetailsEvent? ParseSessionDetails(string? txEventsJson)
246+
{
247+
var attrs = FindEvent(txEventsJson, EventTypes.SessionUpdateDetails);
248+
if (attrs is null) return null;
249+
return new SessionDetailsEvent(
250+
SessionId: ParseLong(attrs.GetValueOrDefault("session_id") ?? attrs.GetValueOrDefault("id")),
251+
AccAddress: attrs.GetValueOrDefault("acc_address") ?? "",
252+
NodeAddress: attrs.GetValueOrDefault("node_address") ?? "",
253+
DownloadBytes: attrs.GetValueOrDefault("download_bytes") ?? "0",
254+
UploadBytes: attrs.GetValueOrDefault("upload_bytes") ?? "0",
255+
Duration: attrs.GetValueOrDefault("duration") ?? "0"
256+
);
257+
}
258+
259+
/// <summary>Parse a SubscriptionPayEvent from TX result events.</summary>
260+
public static SubscriptionPayEvent? ParseSubscriptionPay(string? txEventsJson)
261+
{
262+
var attrs = FindEvent(txEventsJson, EventTypes.SubscriptionPay);
263+
if (attrs is null) return null;
264+
return new SubscriptionPayEvent(
265+
SubscriptionId: ParseLong(attrs.GetValueOrDefault("subscription_id") ?? attrs.GetValueOrDefault("id")),
266+
PlanId: ParseLong(attrs.GetValueOrDefault("plan_id")),
267+
AccAddress: attrs.GetValueOrDefault("acc_address") ?? "",
268+
ProvAddress: attrs.GetValueOrDefault("prov_address") ?? "",
269+
Payment: attrs.GetValueOrDefault("payment") ?? "0",
270+
StakingReward: attrs.GetValueOrDefault("staking_reward") ?? "0"
271+
);
272+
}
273+
274+
/// <summary>Parse a SubscriptionEndEvent from TX result events.</summary>
275+
public static SubscriptionEndEvent? ParseSubscriptionEnd(string? txEventsJson)
276+
{
277+
var attrs = FindEvent(txEventsJson, EventTypes.SubscriptionEnd);
278+
if (attrs is null) return null;
279+
return new SubscriptionEndEvent(
280+
SubscriptionId: ParseLong(attrs.GetValueOrDefault("subscription_id") ?? attrs.GetValueOrDefault("id")),
281+
PlanId: ParseLong(attrs.GetValueOrDefault("plan_id")),
282+
AccAddress: attrs.GetValueOrDefault("acc_address") ?? ""
283+
);
284+
}
285+
286+
/// <summary>Parse a LeaseEndEvent from TX result events.</summary>
287+
public static LeaseEndEvent? ParseLeaseEnd(string? txEventsJson)
288+
{
289+
var attrs = FindEvent(txEventsJson, EventTypes.LeaseEnd);
290+
if (attrs is null) return null;
291+
return new LeaseEndEvent(
292+
LeaseId: ParseLong(attrs.GetValueOrDefault("lease_id") ?? attrs.GetValueOrDefault("id")),
293+
NodeAddress: attrs.GetValueOrDefault("node_address") ?? "",
294+
ProvAddress: attrs.GetValueOrDefault("prov_address") ?? ""
295+
);
296+
}
297+
201298
// ─── Helpers ───────────────────────────────────────────────────
202299

203300
private static long? TryExtractSessionIdFromEvent(JsonElement ev)

src/Sentinel.SDK.Core/RpcClient.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,60 @@ public async Task<List<ChainNode>> QueryNodesForPlanAsync(ulong planId, int stat
189189
.ToList();
190190
}
191191

192+
/// <summary>Query sessions for an account via RPC.</summary>
193+
/// <param name="address">Account address (sent1...).</param>
194+
/// <param name="limit">Maximum sessions to return.</param>
195+
/// <param name="ct">Cancellation token.</param>
196+
/// <returns>Raw protobuf session bytes (Any-encoded).</returns>
197+
public async Task<List<byte[]>> QuerySessionsForAccountAsync(string address, int limit = 100, CancellationToken ct = default)
198+
{
199+
using var ms = new MemoryStream();
200+
ProtobufWriter.WriteStringField(ms, 1, address);
201+
using var pag = new MemoryStream();
202+
ProtobufWriter.WriteVarintField(pag, 2, (ulong)limit);
203+
ProtobufWriter.WriteEmbeddedField(ms, 2, pag.ToArray());
204+
var response = await AbciQueryAsync("/sentinel.session.v3.QueryService/QuerySessionsForAccount", ms.ToArray(), ct);
205+
var fields = ProtobufReader.Decode(response);
206+
return ProtobufReader.GetFields(fields, 1).Select(f => f.Data.ToArray()).ToList();
207+
}
208+
209+
/// <summary>Query subscriptions for an account via RPC.</summary>
210+
/// <param name="address">Account address (sent1...).</param>
211+
/// <param name="limit">Maximum subscriptions to return.</param>
212+
/// <param name="ct">Cancellation token.</param>
213+
/// <returns>Raw protobuf subscription bytes.</returns>
214+
public async Task<List<byte[]>> QuerySubscriptionsForAccountAsync(string address, int limit = 100, CancellationToken ct = default)
215+
{
216+
using var ms = new MemoryStream();
217+
ProtobufWriter.WriteStringField(ms, 1, address);
218+
using var pag = new MemoryStream();
219+
ProtobufWriter.WriteVarintField(pag, 2, (ulong)limit);
220+
ProtobufWriter.WriteEmbeddedField(ms, 2, pag.ToArray());
221+
var response = await AbciQueryAsync("/sentinel.subscription.v3.QueryService/QuerySubscriptionsForAccount", ms.ToArray(), ct);
222+
var fields = ProtobufReader.Decode(response);
223+
return ProtobufReader.GetFields(fields, 1).Select(f => f.Data.ToArray()).ToList();
224+
}
225+
226+
/// <summary>Query a single plan by ID via RPC.</summary>
227+
/// <param name="planId">Plan ID.</param>
228+
/// <param name="ct">Cancellation token.</param>
229+
/// <returns>Raw protobuf plan bytes, or null if not found.</returns>
230+
public async Task<byte[]?> QueryPlanAsync(ulong planId, CancellationToken ct = default)
231+
{
232+
try
233+
{
234+
using var ms = new MemoryStream();
235+
ProtobufWriter.WriteVarintField(ms, 1, planId);
236+
var response = await AbciQueryAsync("/sentinel.plan.v3.QueryService/QueryPlan", ms.ToArray(), ct);
237+
var fields = ProtobufReader.Decode(response);
238+
return ProtobufReader.GetField(fields, 1)?.Data.ToArray();
239+
}
240+
catch
241+
{
242+
return null;
243+
}
244+
}
245+
192246
/// <inheritdoc />
193247
public void Dispose() => _http.Dispose();
194248
}

0 commit comments

Comments
 (0)