@@ -20,6 +20,14 @@ public async Task<Balance> GetBalanceAsync(string address, CancellationToken ct
2020 {
2121 ArgumentNullException . ThrowIfNull ( address ) ;
2222
23+ // RPC-first
24+ try
25+ {
26+ return await _rpcClient . QueryBalanceAsync ( address , Constants . Denom , ct ) ;
27+ }
28+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC GetBalance failed, falling back to LCD: { ex . Message } ") ; }
29+
30+ // LCD fallback
2331 var path = $ "/cosmos/bank/v1beta1/balances/{ address } /by_denom?denom={ Constants . Denom } ";
2432 var json = await LcdGetAsync ( path , ct ) ;
2533
@@ -43,6 +51,14 @@ public async Task<Balance> GetBalanceAsync(string address, CancellationToken ct
4351 /// <returns>List of active chain nodes.</returns>
4452 public async Task < List < ChainNode > > GetActiveNodesAsync ( int limit = 500 , CancellationToken ct = default )
4553 {
54+ // RPC-first
55+ try
56+ {
57+ return await _rpcClient . QueryNodesAsync ( 1 , limit , ct ) ;
58+ }
59+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC GetActiveNodes failed, falling back to LCD: { ex . Message } ") ; }
60+
61+ // LCD fallback
4662 var path = $ "/sentinel/node/v3/nodes?status=1&pagination.limit={ limit } ";
4763 var items = await LcdPaginatedAsync ( path , "nodes" , ct ) ;
4864 return items . Select ( ParseChainNode ) . ToList ( ) ;
@@ -57,6 +73,14 @@ public async Task<List<ChainNode>> GetActiveNodesAsync(int limit = 500, Cancella
5773 {
5874 ArgumentNullException . ThrowIfNull ( nodeAddress ) ;
5975
76+ // RPC-first
77+ try
78+ {
79+ return await _rpcClient . QueryNodeAsync ( nodeAddress , ct ) ;
80+ }
81+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC GetNode failed, falling back to LCD: { ex . Message } ") ; }
82+
83+ // LCD fallback
6084 try
6185 {
6286 var path = $ "/sentinel/node/v3/nodes/{ nodeAddress } ";
@@ -84,6 +108,14 @@ public async Task<List<Subscription>> GetSubscriptionsAsync(string address, Canc
84108 {
85109 ArgumentNullException . ThrowIfNull ( address ) ;
86110
111+ // RPC-first
112+ try
113+ {
114+ return await _rpcClient . QuerySubscriptionsForAccountAsync ( address , ct : ct ) ;
115+ }
116+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC GetSubscriptions failed, falling back to LCD: { ex . Message } ") ; }
117+
118+ // LCD fallback
87119 var path = $ "/sentinel/subscription/v3/accounts/{ address } /subscriptions";
88120 var items = await LcdPaginatedAsync ( path , "subscriptions" , ct ) ;
89121 return items . Select ( ParseSubscription ) . ToList ( ) ;
@@ -99,7 +131,18 @@ public async Task<List<ChainSession>> GetSessionsAsync(string address, string st
99131 {
100132 ArgumentNullException . ThrowIfNull ( address ) ;
101133
102- // MUST use /accounts/{addr}/sessions — query-param format returns ALL sessions unfiltered
134+ // RPC-first
135+ try
136+ {
137+ var sessions = await _rpcClient . QuerySessionsForAccountAsync ( address , ct : ct ) ;
138+ // Filter by status if specified (RPC returns all statuses)
139+ if ( status == "1" )
140+ return sessions . Where ( s => s . Status == "active" || s . Status == "1" ) . ToList ( ) ;
141+ return sessions ;
142+ }
143+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC GetSessions failed, falling back to LCD: { ex . Message } ") ; }
144+
145+ // LCD fallback — MUST use /accounts/{addr}/sessions path
103146 var path = $ "/sentinel/session/v3/accounts/{ address } /sessions?status={ status } ";
104147 var items = await LcdPaginatedAsync ( path , "sessions" , ct ) ;
105148 return items . Select ( ParseChainSession ) . ToList ( ) ;
@@ -113,6 +156,14 @@ public async Task<List<ChainSession>> GetSessionsAsync(string address, string st
113156 /// <returns>List of nodes in the plan.</returns>
114157 public async Task < List < ChainNode > > GetPlanNodesAsync ( int planId , CancellationToken ct = default )
115158 {
159+ // RPC-first
160+ try
161+ {
162+ return await _rpcClient . QueryNodesForPlanAsync ( ( ulong ) planId , 1 , 5000 , ct ) ;
163+ }
164+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC GetPlanNodes failed, falling back to LCD: { ex . Message } ") ; }
165+
166+ // LCD fallback
116167 var path = $ "/sentinel/node/v3/plans/{ planId } /nodes?pagination.limit=5000";
117168 var items = await LcdPaginatedAsync ( path , "nodes" , ct ) ;
118169 return items . Select ( ParseChainNode ) . ToList ( ) ;
@@ -347,14 +398,27 @@ public async Task<IReadOnlyList<ActiveSession>> QueryActiveSessionsForAddressAsy
347398 {
348399 ArgumentNullException . ThrowIfNull ( walletAddress ) ;
349400
350- // MUST use /accounts/{addr}/sessions — the query-param format returns ALL sessions unfiltered
401+ // RPC-first
402+ try
403+ {
404+ var rpcSessions = await _rpcClient . QuerySessionsForAccountAsync ( walletAddress , ct : ct ) ;
405+ return rpcSessions
406+ . Where ( s => s . Status == "active" || s . Status == "1" )
407+ . Select ( s => new ActiveSession (
408+ ulong . TryParse ( s . Id , out var sid ) ? sid : 0 ,
409+ s . NodeAddress ,
410+ SessionStatus . Active ) )
411+ . ToList ( ) ;
412+ }
413+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC QueryActiveSessions failed, falling back to LCD: { ex . Message } ") ; }
414+
415+ // LCD fallback
351416 var path = $ "/sentinel/session/v3/accounts/{ walletAddress } /sessions?status=1";
352417 var items = await LcdPaginatedAsync ( path , "sessions" , ct ) ;
353418 var sessions = new List < ActiveSession > ( ) ;
354419
355420 foreach ( var s in items )
356421 {
357- // v3 sessions wrap fields in base_session
358422 var bs = s . TryGetProperty ( "base_session" , out var baseEl ) ? baseEl : s ;
359423 var idStr = bs . TryGetProperty ( "id" , out var idEl ) ? idEl . GetString ( ) ?? "0" : "0" ;
360424 var nodeAddr = bs . TryGetProperty ( "node_address" , out var naEl ) ? naEl . GetString ( ) ?? "" : "" ;
@@ -375,6 +439,15 @@ public async Task<IReadOnlyList<ActiveSession>> QueryActiveSessionsForAddressAsy
375439 /// <inheritdoc />
376440 public async Task < RawSessionAllocation ? > QuerySessionAllocationAsync ( ulong sessionId , CancellationToken ct = default )
377441 {
442+ // RPC-first
443+ try
444+ {
445+ var result = await _rpcClient . QuerySessionAllocationAsync ( sessionId , ct ) ;
446+ if ( result is not null ) return result ;
447+ }
448+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC QuerySessionAllocation failed, falling back to LCD: { ex . Message } ") ; }
449+
450+ // LCD fallback
378451 var path = $ "/sentinel/session/v3/sessions/{ sessionId } /allocations";
379452 var json = await LcdGetAsync ( path , ct ) ;
380453
@@ -408,8 +481,14 @@ public async Task<IReadOnlyList<ActiveSession>> QueryActiveSessionsForAddressAsy
408481 public async Task < List < SubscriptionAllocation > > QuerySubscriptionAllocationsAsync (
409482 ulong subscriptionId , CancellationToken ct = default )
410483 {
411- // NOTE: v3 endpoint for this specific query hasn't been implemented yet (returns 501).
412- // The v2 path works and returns the same allocation data. Same situation as /plan/v3/plans/{id}.
484+ // RPC-first (v2 — v3 returns 501)
485+ try
486+ {
487+ return await _rpcClient . QuerySubscriptionAllocationsAsync ( subscriptionId , ct : ct ) ;
488+ }
489+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC QuerySubAllocations failed, falling back to LCD: { ex . Message } ") ; }
490+
491+ // LCD fallback (v2 path — v3 returns 501)
413492 var path = $ "/sentinel/subscription/v2/subscriptions/{ subscriptionId } /allocations";
414493 var allocations = new List < SubscriptionAllocation > ( ) ;
415494
@@ -453,6 +532,14 @@ public async Task<List<SubscriptionAllocation>> QuerySubscriptionAllocationsAsyn
453532 /// <returns>List of nodes in the plan.</returns>
454533 public async Task < List < ChainNode > > QueryPlanNodesAsync ( int planId , CancellationToken ct = default )
455534 {
535+ // RPC-first
536+ try
537+ {
538+ return await _rpcClient . QueryNodesForPlanAsync ( ( ulong ) planId , 1 , 5000 , ct ) ;
539+ }
540+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC QueryPlanNodes failed, falling back to LCD: { ex . Message } ") ; }
541+
542+ // LCD fallback
456543 var path = $ "/sentinel/node/v3/plans/{ planId } /nodes?pagination.limit=5000";
457544 var items = await LcdPaginatedAsync ( path , "nodes" , ct ) ;
458545 return items . Select ( ParseChainNode ) . ToList ( ) ;
@@ -546,6 +633,15 @@ public async Task<IReadOnlyList<ChainNode>> GetAvailableNodesAsync(string wallet
546633 {
547634 ArgumentNullException . ThrowIfNull ( provAddress ) ;
548635
636+ // RPC-first (v2 — provider not migrated to v3)
637+ try
638+ {
639+ var result = await _rpcClient . QueryProviderAsync ( provAddress , ct ) ;
640+ if ( result is not null ) return result ;
641+ }
642+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC GetProvider failed, falling back to LCD: { ex . Message } ") ; }
643+
644+ // LCD fallback
549645 try
550646 {
551647 var path = $ "/sentinel/provider/v2/providers/{ provAddress } ";
@@ -584,6 +680,17 @@ public async Task<IReadOnlyList<ChainNode>> GetAvailableNodesAsync(string wallet
584680 {
585681 ArgumentNullException . ThrowIfNull ( id ) ;
586682
683+ // RPC-first
684+ if ( ulong . TryParse ( id , out var subId ) )
685+ {
686+ try
687+ {
688+ return await _rpcClient . QuerySubscriptionAsync ( subId , ct ) ;
689+ }
690+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC GetSubscription failed, falling back to LCD: { ex . Message } ") ; }
691+ }
692+
693+ // LCD fallback
587694 try
588695 {
589696 var path = $ "/sentinel/subscription/v3/subscriptions/{ id } ";
@@ -813,6 +920,14 @@ public async Task<IReadOnlyList<AuthzGrant>> QueryAuthzGrantsAsync(
813920 ArgumentException . ThrowIfNullOrWhiteSpace ( granter ) ;
814921 ArgumentException . ThrowIfNullOrWhiteSpace ( grantee ) ;
815922
923+ // RPC-first
924+ try
925+ {
926+ return await _rpcClient . QueryAuthzGrantsAsync ( granter , grantee , ct : ct ) ;
927+ }
928+ catch ( Exception ex ) { _logger ? . Debug ( $ "RPC QueryAuthzGrants failed, falling back to LCD: { ex . Message } ") ; }
929+
930+ // LCD fallback
816931 var path = $ "/cosmos/authz/v1beta1/grants?granter={ granter } &grantee={ grantee } ";
817932 var grants = new List < AuthzGrant > ( ) ;
818933
@@ -833,7 +948,6 @@ public async Task<IReadOnlyList<AuthzGrant>> QueryAuthzGrantsAsync(
833948 if ( auth . TryGetProperty ( "@type" , out var typeProp ) )
834949 msgTypeUrl = typeProp . GetString ( ) ?? "" ;
835950
836- // GenericAuthorization has msg field
837951 if ( auth . TryGetProperty ( "msg" , out var msgProp ) )
838952 msgTypeUrl = msgProp . GetString ( ) ?? msgTypeUrl ;
839953 }
0 commit comments