Skip to main content

blokli_client/api/v1/graphql/
curvy.rs

1//! Cynic types for Curvy indexing, synchronization, contract reads, and subscriptions.
2//!
3//! Ownership is deliberately not decided by Blokli. Consumers pass pending-note
4//! metadata to the Curvy SDK scanner and retain only note IDs owned by the node.
5
6use super::{Hex32, InvalidAddressError, QueryFailedError, Uint64, Uint256, schema};
7use crate::errors::{BlokliClientError, ErrorKind};
8
9/// Exclusive chain-position cursor used by paginated Curvy event queries.
10#[derive(cynic::InputObject, Clone, Debug, Eq, PartialEq)]
11pub struct CurvyEventCursor {
12    pub block: Uint64,
13    pub transaction_index: Uint64,
14    pub log_index: Uint64,
15    pub event_item_index: Uint64,
16    pub block_hash: Option<Hex32>,
17}
18
19impl CurvyEventCursor {
20    /// Creates an unanchored exclusive cursor from chain-position components.
21    pub fn new(block: u64, transaction_index: u64, log_index: u64, event_item_index: u64) -> Self {
22        Self {
23            block: Uint64(block.to_string()),
24            transaction_index: Uint64(transaction_index.to_string()),
25            log_index: Uint64(log_index.to_string()),
26            event_item_index: Uint64(event_item_index.to_string()),
27            block_hash: None,
28        }
29    }
30}
31
32/// Canonical chain position and transaction identity for a Curvy event item.
33#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
34#[cfg_attr(feature = "serde", derive(serde::Serialize))]
35pub struct CurvyEventPosition {
36    pub transaction_hash: Hex32,
37    pub block_hash: Hex32,
38    pub block: Uint64,
39    pub transaction_index: Uint64,
40    pub log_index: Uint64,
41    pub event_item_index: Uint64,
42}
43
44impl From<&CurvyEventPosition> for CurvyEventCursor {
45    fn from(position: &CurvyEventPosition) -> Self {
46        Self {
47            block: position.block.clone(),
48            transaction_index: position.transaction_index.clone(),
49            log_index: position.log_index.clone(),
50            event_item_index: position.event_item_index.clone(),
51            block_hash: Some(position.block_hash.clone()),
52        }
53    }
54}
55
56/// One pending-note announcement. Feed this metadata to the Curvy SDK ownership scanner.
57#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
58#[cfg_attr(feature = "serde", derive(serde::Serialize))]
59pub struct CurvyPendingNote {
60    pub note_id: Hex32,
61    pub ephemeral_key: Vec<Uint256>,
62    pub view_tag: i32,
63    pub token_id: Uint256,
64    pub amount: Uint256,
65    pub is_plaintext: bool,
66    pub position: CurvyEventPosition,
67}
68
69/// One committed note.
70#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
71#[cfg_attr(feature = "serde", derive(serde::Serialize))]
72pub struct CurvyCommittedNote {
73    pub batch_index: Hex32,
74    pub note_id: Hex32,
75    pub leaf_index: Uint64,
76    pub position: CurvyEventPosition,
77}
78
79/// One committed nullifier.
80#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
81#[cfg_attr(feature = "serde", derive(serde::Serialize))]
82pub struct CurvyCommittedNullifier {
83    pub batch_index: Hex32,
84    pub nullifier: Hex32,
85    pub nullifier_index: Uint64,
86    pub position: CurvyEventPosition,
87}
88
89#[derive(cynic::QueryVariables, Clone, Debug, Default)]
90pub struct CurvyEventPageVariables {
91    pub from_block: Option<Uint64>,
92    pub after: Option<CurvyEventCursor>,
93    pub first: Option<i32>,
94}
95
96#[derive(cynic::QueryVariables, Clone, Debug, Default)]
97pub struct CurvyEventSubscriptionVariables {
98    pub from_block: Option<Uint64>,
99}
100
101#[derive(cynic::QueryFragment, Debug)]
102#[cynic(graphql_type = "QueryRoot", variables = "CurvyEventPageVariables")]
103pub struct QueryCurvyPendingNotes {
104    #[arguments(fromBlock: $from_block, after: $after, first: $first)]
105    pub curvy_pending_notes: CurvyPendingNotesResult,
106}
107
108#[derive(cynic::QueryFragment, Debug)]
109#[cynic(graphql_type = "QueryRoot", variables = "CurvyEventPageVariables")]
110pub struct QueryCurvyCommittedNotes {
111    #[arguments(fromBlock: $from_block, after: $after, first: $first)]
112    pub curvy_committed_notes: CurvyCommittedNotesResult,
113}
114
115#[derive(cynic::QueryFragment, Debug)]
116#[cynic(graphql_type = "QueryRoot", variables = "CurvyEventPageVariables")]
117pub struct QueryCurvyCommittedNullifiers {
118    #[arguments(fromBlock: $from_block, after: $after, first: $first)]
119    pub curvy_committed_nullifiers: CurvyCommittedNullifiersResult,
120}
121
122#[derive(cynic::QueryFragment, Debug)]
123#[cynic(graphql_type = "SubscriptionRoot", variables = "CurvyEventSubscriptionVariables")]
124pub struct SubscribeCurvyPendingNote {
125    #[arguments(fromBlock: $from_block)]
126    pub curvy_pending_note: CurvyPendingNote,
127}
128
129#[derive(cynic::QueryFragment, Debug)]
130#[cynic(graphql_type = "SubscriptionRoot", variables = "CurvyEventSubscriptionVariables")]
131pub struct SubscribeCurvyCommittedNote {
132    #[arguments(fromBlock: $from_block)]
133    pub curvy_committed_note: CurvyCommittedNote,
134}
135
136#[derive(cynic::QueryFragment, Debug)]
137#[cynic(graphql_type = "SubscriptionRoot", variables = "CurvyEventSubscriptionVariables")]
138pub struct SubscribeCurvyCommittedNullifier {
139    #[arguments(fromBlock: $from_block)]
140    pub curvy_committed_nullifier: CurvyCommittedNullifier,
141}
142
143#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
144#[cfg_attr(feature = "serde", derive(serde::Serialize))]
145pub struct CurvyPendingNotes {
146    pub notes: Vec<CurvyPendingNote>,
147}
148
149#[derive(cynic::InlineFragments, Debug)]
150pub enum CurvyPendingNotesResult {
151    CurvyPendingNotes(CurvyPendingNotes),
152    QueryFailedError(QueryFailedError),
153    #[cynic(fallback)]
154    Unknown,
155}
156
157#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
158#[cfg_attr(feature = "serde", derive(serde::Serialize))]
159pub struct CurvyCommittedNotes {
160    pub notes: Vec<CurvyCommittedNote>,
161}
162
163#[derive(cynic::InlineFragments, Debug)]
164pub enum CurvyCommittedNotesResult {
165    CurvyCommittedNotes(CurvyCommittedNotes),
166    QueryFailedError(QueryFailedError),
167    #[cynic(fallback)]
168    Unknown,
169}
170
171#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
172#[cfg_attr(feature = "serde", derive(serde::Serialize))]
173pub struct CurvyCommittedNullifiers {
174    pub nullifiers: Vec<CurvyCommittedNullifier>,
175}
176
177#[derive(cynic::InlineFragments, Debug)]
178pub enum CurvyCommittedNullifiersResult {
179    CurvyCommittedNullifiers(CurvyCommittedNullifiers),
180    QueryFailedError(QueryFailedError),
181    #[cynic(fallback)]
182    Unknown,
183}
184
185/// Finalized Curvy synchronization checkpoint.
186#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
187#[cfg_attr(feature = "serde", derive(serde::Serialize))]
188pub struct CurvySyncCheckpoint {
189    pub block_number: Uint64,
190    pub block_hash: Hex32,
191    pub aggregator_address: String,
192    pub tree_version: i32,
193    pub tree_depth: i32,
194    pub shard_height: i32,
195    pub shard_size: Uint64,
196    pub note_count: Uint64,
197    pub nullifier_count: Uint64,
198    pub shard_count: Uint64,
199    pub notes_root: Hex32,
200}
201
202#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
203#[cfg_attr(feature = "serde", derive(serde::Serialize))]
204pub struct CurvySyncNote {
205    pub leaf_index: Uint64,
206    pub note_id: Hex32,
207    pub batch_index: Hex32,
208    pub announcement: Option<CurvyPendingNote>,
209    pub commit_position: CurvyEventPosition,
210}
211
212#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
213#[cfg_attr(feature = "serde", derive(serde::Serialize))]
214pub struct CurvySyncNotePage {
215    pub checkpoint: Hex32,
216    pub notes: Vec<CurvySyncNote>,
217    pub next_index: Uint64,
218    pub total: Uint64,
219}
220
221#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
222#[cfg_attr(feature = "serde", derive(serde::Serialize))]
223pub struct CurvySyncNullifierPage {
224    pub checkpoint: Hex32,
225    pub nullifiers: Vec<CurvyCommittedNullifier>,
226    pub next_index: Uint64,
227    pub total: Uint64,
228}
229
230#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
231#[cfg_attr(feature = "serde", derive(serde::Serialize))]
232pub struct CurvyShardRoot {
233    pub shard_index: Uint64,
234    pub root: Hex32,
235    pub completion_position: CurvyEventPosition,
236}
237
238#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
239#[cfg_attr(feature = "serde", derive(serde::Serialize))]
240pub struct CurvyShardRootPage {
241    pub checkpoint: Hex32,
242    pub shard_roots: Vec<CurvyShardRoot>,
243    pub next_index: Uint64,
244    pub total: Uint64,
245}
246
247#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
248#[cfg_attr(feature = "serde", derive(serde::Serialize))]
249pub struct CurvyAggregatorState {
250    pub notes_tree_root: Hex32,
251    pub notes_batch_index: Uint256,
252    pub nullifiers_batch_index: Uint256,
253    pub note_index: Uint256,
254}
255
256#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
257#[cfg_attr(feature = "serde", derive(serde::Serialize))]
258pub struct CurvyNoteStatus {
259    pub status: i32,
260}
261
262#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
263#[cfg_attr(feature = "serde", derive(serde::Serialize))]
264pub struct CurvyBooleanValue {
265    pub value: bool,
266}
267
268#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
269#[cfg_attr(feature = "serde", derive(serde::Serialize))]
270pub struct CurvyVaultFees {
271    pub deposit_fee: Uint256,
272    pub withdrawal_fee: Uint256,
273}
274
275#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
276#[cfg_attr(feature = "serde", derive(serde::Serialize))]
277pub struct CurvyAggregatorFees {
278    pub protocol_fee_per_thousand: Uint256,
279    pub commitment_fee_root: Hex32,
280    pub fee_note_public_key: Vec<Uint256>,
281}
282
283#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
284#[cfg_attr(feature = "serde", derive(serde::Serialize))]
285pub struct CurvyGasFees {
286    pub token_id: Uint256,
287    pub portal_deployment: Uint256,
288    pub pending_note_commitment: Uint256,
289    pub withdrawal: Uint256,
290}
291
292#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
293#[cfg_attr(feature = "serde", derive(serde::Serialize))]
294pub struct CurvyVaultToken {
295    pub token_address: String,
296    pub gas_fees: CurvyGasFees,
297}
298
299#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
300#[cfg_attr(feature = "serde", derive(serde::Serialize))]
301pub struct CurvyVaultTokenCount {
302    pub count: Uint256,
303}
304
305#[derive(cynic::QueryFragment, Clone, Debug, Eq, PartialEq)]
306#[cfg_attr(feature = "serde", derive(serde::Serialize))]
307pub struct CurvyAddress {
308    pub address: String,
309}
310
311#[derive(cynic::QueryVariables, Clone, Debug, Default)]
312pub struct CurvyCheckpointVariables {
313    pub block_hash: Option<Hex32>,
314}
315
316#[derive(cynic::QueryVariables, Clone, Debug)]
317pub struct CurvySyncPageVariables {
318    pub checkpoint: Hex32,
319    pub from_index: Option<Uint64>,
320    pub first: Option<i32>,
321}
322
323#[derive(cynic::QueryVariables, Clone, Debug)]
324pub struct CurvyNoteIdVariables {
325    pub note_id: Hex32,
326}
327
328#[derive(cynic::QueryVariables, Clone, Debug)]
329pub struct CurvyRootVariables {
330    pub root: Hex32,
331}
332
333#[derive(cynic::QueryVariables, Clone, Debug)]
334pub struct CurvyNullifierVariables {
335    pub nullifier: Hex32,
336}
337
338#[derive(cynic::QueryVariables, Clone, Debug)]
339pub struct CurvyVaultTokenVariables {
340    pub token_id: Uint256,
341}
342
343#[derive(cynic::QueryVariables, Clone, Debug)]
344pub struct CurvyEntryPortalVariables {
345    pub owner_hash: Uint256,
346    pub recovery: String,
347}
348
349#[derive(cynic::QueryVariables, Clone, Debug)]
350pub struct CurvyExitPortalVariables {
351    pub exit_address: String,
352    pub exit_chain_id: Uint256,
353    pub recovery: String,
354}
355
356#[derive(cynic::QueryVariables, Clone, Debug)]
357pub struct CurvyPortalVariables {
358    pub portal_address: String,
359}
360
361#[derive(cynic::QueryFragment, Debug)]
362#[cynic(graphql_type = "QueryRoot", variables = "CurvyCheckpointVariables")]
363pub struct QueryCurvySyncCheckpoint {
364    #[arguments(blockHash: $block_hash)]
365    pub curvy_sync_checkpoint: CurvySyncCheckpointResult,
366}
367
368#[derive(cynic::QueryFragment, Debug)]
369#[cynic(graphql_type = "QueryRoot", variables = "CurvySyncPageVariables")]
370pub struct QueryCurvySyncNotes {
371    #[arguments(checkpoint: $checkpoint, fromIndex: $from_index, first: $first)]
372    pub curvy_sync_notes: CurvySyncNotesResult,
373}
374
375#[derive(cynic::QueryFragment, Debug)]
376#[cynic(graphql_type = "QueryRoot", variables = "CurvySyncPageVariables")]
377pub struct QueryCurvySyncNullifiers {
378    #[arguments(checkpoint: $checkpoint, fromIndex: $from_index, first: $first)]
379    pub curvy_sync_nullifiers: CurvySyncNullifiersResult,
380}
381
382#[derive(cynic::QueryFragment, Debug)]
383#[cynic(graphql_type = "QueryRoot", variables = "CurvySyncPageVariables")]
384pub struct QueryCurvyShardRoots {
385    #[arguments(checkpoint: $checkpoint, fromIndex: $from_index, first: $first)]
386    pub curvy_shard_roots: CurvyShardRootsResult,
387}
388
389#[derive(cynic::QueryFragment, Debug)]
390#[cynic(graphql_type = "QueryRoot")]
391pub struct QueryCurvyAggregatorState {
392    pub curvy_aggregator_state: CurvyAggregatorStateResult,
393}
394
395#[derive(cynic::QueryFragment, Debug)]
396#[cynic(graphql_type = "QueryRoot", variables = "CurvyNoteIdVariables")]
397pub struct QueryCurvyNoteStatus {
398    #[arguments(noteId: $note_id)]
399    pub curvy_note_status: CurvyNoteStatusResult,
400}
401
402#[derive(cynic::QueryFragment, Debug)]
403#[cynic(graphql_type = "QueryRoot", variables = "CurvyRootVariables")]
404pub struct QueryCurvyValidNotesRoot {
405    #[arguments(root: $root)]
406    pub curvy_valid_notes_root: CurvyValidNotesRootResult,
407}
408
409#[derive(cynic::QueryFragment, Debug)]
410#[cynic(graphql_type = "QueryRoot", variables = "CurvyNullifierVariables")]
411pub struct QueryCurvyNullifierSpent {
412    #[arguments(nullifier: $nullifier)]
413    pub curvy_nullifier_spent: CurvyNullifierSpentResult,
414}
415
416#[derive(cynic::QueryFragment, Debug)]
417#[cynic(graphql_type = "QueryRoot")]
418pub struct QueryCurvyVaultFees {
419    pub curvy_vault_fees: CurvyVaultFeesResult,
420}
421
422#[derive(cynic::QueryFragment, Debug)]
423#[cynic(graphql_type = "QueryRoot")]
424pub struct QueryCurvyAggregatorFees {
425    pub curvy_aggregator_fees: CurvyAggregatorFeesResult,
426}
427
428#[derive(cynic::QueryFragment, Debug)]
429#[cynic(graphql_type = "QueryRoot")]
430pub struct QueryCurvyVaultTokenCount {
431    pub curvy_vault_token_count: CurvyVaultTokenCountResult,
432}
433
434#[derive(cynic::QueryFragment, Debug)]
435#[cynic(graphql_type = "QueryRoot", variables = "CurvyVaultTokenVariables")]
436pub struct QueryCurvyVaultToken {
437    #[arguments(tokenId: $token_id)]
438    pub curvy_vault_token: CurvyVaultTokenResult,
439}
440
441#[derive(cynic::QueryFragment, Debug)]
442#[cynic(graphql_type = "QueryRoot", variables = "CurvyEntryPortalVariables")]
443pub struct QueryCurvyEntryPortalAddress {
444    #[arguments(ownerHash: $owner_hash, recovery: $recovery)]
445    pub curvy_entry_portal_address: CurvyEntryPortalAddressResult,
446}
447
448#[derive(cynic::QueryFragment, Debug)]
449#[cynic(graphql_type = "QueryRoot", variables = "CurvyExitPortalVariables")]
450pub struct QueryCurvyExitPortalAddress {
451    #[arguments(exitAddress: $exit_address, exitChainId: $exit_chain_id, recovery: $recovery)]
452    pub curvy_exit_portal_address: CurvyExitPortalAddressResult,
453}
454
455#[derive(cynic::QueryFragment, Debug)]
456#[cynic(graphql_type = "QueryRoot", variables = "CurvyPortalVariables")]
457pub struct QueryCurvyPortalRegistered {
458    #[arguments(portalAddress: $portal_address)]
459    pub curvy_portal_registered: CurvyPortalRegisteredResult,
460}
461
462macro_rules! simple_union {
463    ($name:ident, $success:ident, $value:ty) => {
464        #[derive(cynic::InlineFragments, Debug)]
465        pub enum $name {
466            $success($value),
467            QueryFailedError(QueryFailedError),
468            #[cynic(fallback)]
469            Unknown,
470        }
471
472        impl From<$name> for Result<$value, BlokliClientError> {
473            fn from(value: $name) -> Self {
474                match value {
475                    $name::$success(value) => Ok(value),
476                    $name::QueryFailedError(error) => Err(error.into()),
477                    $name::Unknown => Err(ErrorKind::NoData.into()),
478                }
479            }
480        }
481    };
482}
483
484simple_union!(CurvySyncCheckpointResult, CurvySyncCheckpoint, CurvySyncCheckpoint);
485simple_union!(CurvySyncNotesResult, CurvySyncNotePage, CurvySyncNotePage);
486simple_union!(
487    CurvySyncNullifiersResult,
488    CurvySyncNullifierPage,
489    CurvySyncNullifierPage
490);
491simple_union!(CurvyShardRootsResult, CurvyShardRootPage, CurvyShardRootPage);
492simple_union!(CurvyAggregatorStateResult, CurvyAggregatorState, CurvyAggregatorState);
493simple_union!(CurvyNoteStatusResult, CurvyNoteStatus, CurvyNoteStatus);
494simple_union!(CurvyValidNotesRootResult, CurvyBooleanValue, CurvyBooleanValue);
495simple_union!(CurvyNullifierSpentResult, CurvyBooleanValue, CurvyBooleanValue);
496simple_union!(CurvyVaultFeesResult, CurvyVaultFees, CurvyVaultFees);
497simple_union!(CurvyAggregatorFeesResult, CurvyAggregatorFees, CurvyAggregatorFees);
498simple_union!(CurvyVaultTokenCountResult, CurvyVaultTokenCount, CurvyVaultTokenCount);
499simple_union!(CurvyVaultTokenResult, CurvyVaultToken, CurvyVaultToken);
500
501macro_rules! address_union {
502    ($name:ident) => {
503        #[derive(cynic::InlineFragments, Debug)]
504        pub enum $name {
505            CurvyAddress(CurvyAddress),
506            InvalidAddressError(InvalidAddressError),
507            QueryFailedError(QueryFailedError),
508            #[cynic(fallback)]
509            Unknown,
510        }
511
512        impl From<$name> for Result<CurvyAddress, BlokliClientError> {
513            fn from(value: $name) -> Self {
514                match value {
515                    $name::CurvyAddress(value) => Ok(value),
516                    $name::InvalidAddressError(error) => Err(error.into()),
517                    $name::QueryFailedError(error) => Err(error.into()),
518                    $name::Unknown => Err(ErrorKind::NoData.into()),
519                }
520            }
521        }
522    };
523}
524
525address_union!(CurvyEntryPortalAddressResult);
526address_union!(CurvyExitPortalAddressResult);
527
528#[derive(cynic::InlineFragments, Debug)]
529pub enum CurvyPortalRegisteredResult {
530    CurvyBooleanValue(CurvyBooleanValue),
531    InvalidAddressError(InvalidAddressError),
532    QueryFailedError(QueryFailedError),
533    #[cynic(fallback)]
534    Unknown,
535}
536
537impl From<CurvyPortalRegisteredResult> for Result<CurvyBooleanValue, BlokliClientError> {
538    fn from(value: CurvyPortalRegisteredResult) -> Self {
539        match value {
540            CurvyPortalRegisteredResult::CurvyBooleanValue(value) => Ok(value),
541            CurvyPortalRegisteredResult::InvalidAddressError(error) => Err(error.into()),
542            CurvyPortalRegisteredResult::QueryFailedError(error) => Err(error.into()),
543            CurvyPortalRegisteredResult::Unknown => Err(ErrorKind::NoData.into()),
544        }
545    }
546}
547
548impl From<CurvyPendingNotesResult> for Result<CurvyPendingNotes, BlokliClientError> {
549    fn from(value: CurvyPendingNotesResult) -> Self {
550        match value {
551            CurvyPendingNotesResult::CurvyPendingNotes(notes) => Ok(notes),
552            CurvyPendingNotesResult::QueryFailedError(error) => Err(error.into()),
553            CurvyPendingNotesResult::Unknown => Err(ErrorKind::NoData.into()),
554        }
555    }
556}
557
558impl From<CurvyCommittedNotesResult> for Result<CurvyCommittedNotes, BlokliClientError> {
559    fn from(value: CurvyCommittedNotesResult) -> Self {
560        match value {
561            CurvyCommittedNotesResult::CurvyCommittedNotes(notes) => Ok(notes),
562            CurvyCommittedNotesResult::QueryFailedError(error) => Err(error.into()),
563            CurvyCommittedNotesResult::Unknown => Err(ErrorKind::NoData.into()),
564        }
565    }
566}
567
568impl From<CurvyCommittedNullifiersResult> for Result<CurvyCommittedNullifiers, BlokliClientError> {
569    fn from(value: CurvyCommittedNullifiersResult) -> Self {
570        match value {
571            CurvyCommittedNullifiersResult::CurvyCommittedNullifiers(nullifiers) => Ok(nullifiers),
572            CurvyCommittedNullifiersResult::QueryFailedError(error) => Err(error.into()),
573            CurvyCommittedNullifiersResult::Unknown => Err(ErrorKind::NoData.into()),
574        }
575    }
576}
577
578#[cfg(test)]
579mod tests {
580    use std::fmt::Debug;
581
582    use super::{
583        CurvyAddress, CurvyBooleanValue, CurvyCommittedNotes, CurvyCommittedNotesResult, CurvyCommittedNullifiers,
584        CurvyCommittedNullifiersResult, CurvyEntryPortalAddressResult, CurvyEventCursor, CurvyEventPosition,
585        CurvyNoteStatus, CurvyNoteStatusResult, CurvyPendingNotes, CurvyPendingNotesResult,
586        CurvyPortalRegisteredResult, Hex32, InvalidAddressError, QueryFailedError, Uint64,
587    };
588    use crate::errors::{BlokliClientError, ErrorKind};
589
590    fn query_failed_error() -> QueryFailedError {
591        QueryFailedError {
592            __typename: "QueryFailedError".to_owned(),
593            message: "query failed".to_owned(),
594            code: "QUERY_FAILED".to_owned(),
595        }
596    }
597
598    fn invalid_address_error() -> InvalidAddressError {
599        InvalidAddressError {
600            __typename: "InvalidAddressError".to_owned(),
601            message: "invalid address".to_owned(),
602            code: "INVALID_ADDRESS".to_owned(),
603        }
604    }
605
606    fn assert_no_data<T: Debug>(result: Result<T, BlokliClientError>) {
607        assert!(matches!(
608            result.expect_err("conversion should fail").kind(),
609            ErrorKind::NoData
610        ));
611    }
612
613    #[test]
614    fn event_position_converts_to_anchored_cursor() {
615        let position = CurvyEventPosition {
616            transaction_hash: Hex32("0xtx".to_owned()),
617            block_hash: Hex32("0xblock".to_owned()),
618            block: Uint64("10".to_owned()),
619            transaction_index: Uint64("2".to_owned()),
620            log_index: Uint64("3".to_owned()),
621            event_item_index: Uint64("4".to_owned()),
622        };
623
624        let cursor = CurvyEventCursor::from(&position);
625
626        assert_eq!(
627            cursor,
628            CurvyEventCursor {
629                block: Uint64("10".to_owned()),
630                transaction_index: Uint64("2".to_owned()),
631                log_index: Uint64("3".to_owned()),
632                event_item_index: Uint64("4".to_owned()),
633                block_hash: Some(Hex32("0xblock".to_owned())),
634            }
635        );
636        assert_eq!(CurvyEventCursor::new(10, 2, 3, 4).block_hash, None);
637    }
638
639    #[test]
640    fn simple_union_converts_success_and_errors() {
641        let status: Result<CurvyNoteStatus, BlokliClientError> =
642            CurvyNoteStatusResult::CurvyNoteStatus(CurvyNoteStatus { status: 2 }).into();
643        assert_eq!(status.expect("status should convert").status, 2);
644
645        let error: Result<CurvyNoteStatus, BlokliClientError> =
646            CurvyNoteStatusResult::QueryFailedError(query_failed_error()).into();
647        assert!(matches!(
648            error.expect_err("query failure should convert").kind(),
649            ErrorKind::BlokliError {
650                kind: "query failed",
651                ..
652            }
653        ));
654
655        assert_no_data(Result::<CurvyNoteStatus, BlokliClientError>::from(
656            CurvyNoteStatusResult::Unknown,
657        ));
658    }
659
660    #[test]
661    fn address_union_converts_every_variant() {
662        let address: Result<CurvyAddress, BlokliClientError> =
663            CurvyEntryPortalAddressResult::CurvyAddress(CurvyAddress {
664                address: "0x1234".to_owned(),
665            })
666            .into();
667        assert_eq!(address.expect("address should convert").address, "0x1234");
668
669        let invalid: Result<CurvyAddress, BlokliClientError> =
670            CurvyEntryPortalAddressResult::InvalidAddressError(invalid_address_error()).into();
671        assert!(matches!(
672            invalid.expect_err("invalid address should convert").kind(),
673            ErrorKind::BlokliError {
674                kind: "invalid address",
675                ..
676            }
677        ));
678
679        let failed: Result<CurvyAddress, BlokliClientError> =
680            CurvyEntryPortalAddressResult::QueryFailedError(query_failed_error()).into();
681        assert!(matches!(
682            failed.expect_err("query failure should convert").kind(),
683            ErrorKind::BlokliError {
684                kind: "query failed",
685                ..
686            }
687        ));
688
689        assert_no_data(Result::<CurvyAddress, BlokliClientError>::from(
690            CurvyEntryPortalAddressResult::Unknown,
691        ));
692    }
693
694    #[test]
695    fn portal_registered_union_converts_every_variant() {
696        let registered: Result<CurvyBooleanValue, BlokliClientError> =
697            CurvyPortalRegisteredResult::CurvyBooleanValue(CurvyBooleanValue { value: true }).into();
698        assert!(registered.expect("boolean should convert").value);
699
700        let invalid: Result<CurvyBooleanValue, BlokliClientError> =
701            CurvyPortalRegisteredResult::InvalidAddressError(invalid_address_error()).into();
702        assert!(matches!(
703            invalid.expect_err("invalid address should convert").kind(),
704            ErrorKind::BlokliError {
705                kind: "invalid address",
706                ..
707            }
708        ));
709
710        let failed: Result<CurvyBooleanValue, BlokliClientError> =
711            CurvyPortalRegisteredResult::QueryFailedError(query_failed_error()).into();
712        assert!(matches!(
713            failed.expect_err("query failure should convert").kind(),
714            ErrorKind::BlokliError {
715                kind: "query failed",
716                ..
717            }
718        ));
719
720        assert_no_data(Result::<CurvyBooleanValue, BlokliClientError>::from(
721            CurvyPortalRegisteredResult::Unknown,
722        ));
723    }
724
725    #[test]
726    fn event_page_unions_convert_success_and_errors() {
727        let pending: Result<CurvyPendingNotes, BlokliClientError> =
728            CurvyPendingNotesResult::CurvyPendingNotes(CurvyPendingNotes { notes: Vec::new() }).into();
729        assert!(pending.expect("pending notes should convert").notes.is_empty());
730        let committed: Result<CurvyCommittedNotes, BlokliClientError> =
731            CurvyCommittedNotesResult::CurvyCommittedNotes(CurvyCommittedNotes { notes: Vec::new() }).into();
732        assert!(committed.expect("committed notes should convert").notes.is_empty());
733        let nullifiers: Result<CurvyCommittedNullifiers, BlokliClientError> =
734            CurvyCommittedNullifiersResult::CurvyCommittedNullifiers(CurvyCommittedNullifiers {
735                nullifiers: Vec::new(),
736            })
737            .into();
738        assert!(nullifiers.expect("nullifiers should convert").nullifiers.is_empty());
739
740        let pending_error: Result<CurvyPendingNotes, BlokliClientError> =
741            CurvyPendingNotesResult::QueryFailedError(query_failed_error()).into();
742        assert!(matches!(
743            pending_error.expect_err("query failure should convert").kind(),
744            ErrorKind::BlokliError { .. }
745        ));
746        let committed_error: Result<CurvyCommittedNotes, BlokliClientError> =
747            CurvyCommittedNotesResult::QueryFailedError(query_failed_error()).into();
748        assert!(matches!(
749            committed_error.expect_err("query failure should convert").kind(),
750            ErrorKind::BlokliError { .. }
751        ));
752        let nullifier_error: Result<CurvyCommittedNullifiers, BlokliClientError> =
753            CurvyCommittedNullifiersResult::QueryFailedError(query_failed_error()).into();
754        assert!(matches!(
755            nullifier_error.expect_err("query failure should convert").kind(),
756            ErrorKind::BlokliError { .. }
757        ));
758
759        assert_no_data(Result::<CurvyPendingNotes, BlokliClientError>::from(
760            CurvyPendingNotesResult::Unknown,
761        ));
762        assert_no_data(Result::<CurvyCommittedNotes, BlokliClientError>::from(
763            CurvyCommittedNotesResult::Unknown,
764        ));
765        assert_no_data(Result::<CurvyCommittedNullifiers, BlokliClientError>::from(
766            CurvyCommittedNullifiersResult::Unknown,
767        ));
768    }
769}