Skip to main content

blokli_client/api/v1/graphql/
safe.rs

1use super::{InvalidAddressError, QueryFailedError, Uint64, schema};
2use crate::{
3    api::ChainAddress,
4    errors::{BlokliClientError, ErrorKind},
5};
6
7/// Safe selector variant sent to the GraphQL API.
8#[derive(cynic::Enum, Clone, Copy, Debug, PartialEq, Eq)]
9pub enum SafeSelectorInput {
10    /// Select by safe contract address.
11    #[cynic(rename = "ADDRESS")]
12    Address,
13    /// Select by owner address.
14    #[cynic(rename = "OWNER")]
15    Owner,
16    /// Select by the owner's chain key legacy alias.
17    #[cynic(rename = "CHAIN_KEY")]
18    ChainKey,
19    /// Select by registered node address.
20    #[cynic(rename = "REGISTERED_NODE")]
21    RegisteredNode,
22}
23
24#[derive(cynic::QueryVariables, Debug)]
25pub struct SafeByVariables {
26    pub selector: SafeSelectorInput,
27    pub address: String,
28}
29
30/// Safe contract indexed by Blokli.
31#[derive(cynic::QueryFragment, Debug, Clone, PartialEq, Eq)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33pub struct Safe {
34    /// Safe contract address encoded as a hex string.
35    pub address: String,
36    /// Chain key associated with the safe.
37    pub chain_key: String,
38    /// Owner addresses encoded as hex strings.
39    pub owners: Vec<String>,
40    /// Module contract address encoded as a hex string.
41    pub module_address: String,
42    /// Registered node addresses encoded as hex strings.
43    pub registered_nodes: Vec<String>,
44    /// Safe signature threshold, when reported by the API.
45    pub threshold: Option<String>,
46}
47
48#[derive(cynic::QueryFragment, Debug)]
49#[cynic(graphql_type = "QueryRoot", variables = "SafeByVariables")]
50pub struct QuerySafeBy {
51    #[arguments(selector: $selector, address: $address)]
52    pub safe_by: Option<SafeByResult>,
53}
54
55#[derive(cynic::QueryFragment, Debug)]
56#[cynic(graphql_type = "SubscriptionRoot")]
57pub struct SubscribeSafeDeployment {
58    pub safe_deployed: Safe,
59}
60
61#[derive(cynic::QueryVariables, Debug)]
62pub struct ModuleAddressVariables {
63    pub nonce: Uint64,
64    pub owner: String,
65    pub safe_address: String,
66}
67
68/// Predicted module address returned by Blokli.
69#[derive(cynic::QueryFragment, Debug, Clone, PartialEq, Eq)]
70pub struct ModuleAddress {
71    /// GraphQL concrete type name.
72    pub __typename: String,
73    /// Predicted module contract address encoded as a hex string.
74    pub module_address: String,
75}
76
77#[derive(cynic::QueryFragment, Debug)]
78#[cynic(graphql_type = "QueryRoot", variables = "ModuleAddressVariables")]
79pub struct QueryModuleAddress {
80    #[arguments(nonce: $nonce, safeAddress: $safe_address, owner: $owner)]
81    pub calculate_module_address: CalculateModuleAddressResult,
82}
83
84/// Result union for deprecated single-safe GraphQL fields (`safe`, `safeByChainKey`, `safeByRegisteredNode`).
85#[derive(cynic::InlineFragments, Debug)]
86pub enum SafeResult {
87    Safe(Safe),
88    InvalidAddressError(InvalidAddressError),
89    QueryFailedError(QueryFailedError),
90    #[cynic(fallback)]
91    Unknown,
92}
93
94impl From<SafeResult> for Result<Option<Safe>, BlokliClientError> {
95    fn from(value: SafeResult) -> Self {
96        match value {
97            SafeResult::Safe(safe) => Ok(Some(safe)),
98            SafeResult::InvalidAddressError(e) => Err(e.into()),
99            SafeResult::QueryFailedError(e) => Err(e.into()),
100            SafeResult::Unknown => Err(ErrorKind::NoData.into()),
101        }
102    }
103}
104
105/// List of safes returned by a safe query.
106#[derive(cynic::QueryFragment, Debug, Clone, PartialEq, Eq)]
107pub struct SafesList {
108    /// Matching safes.
109    pub safes: Vec<Safe>,
110}
111
112#[derive(cynic::InlineFragments, Debug)]
113pub enum SafeByResult {
114    SafesList(SafesList),
115    InvalidAddressError(InvalidAddressError),
116    QueryFailedError(QueryFailedError),
117    #[cynic(fallback)]
118    Unknown,
119}
120
121impl From<SafeByResult> for Result<Vec<Safe>, BlokliClientError> {
122    fn from(value: SafeByResult) -> Self {
123        match value {
124            SafeByResult::SafesList(safes) => Ok(safes.safes),
125            SafeByResult::InvalidAddressError(e) => Err(e.into()),
126            SafeByResult::QueryFailedError(e) => Err(e.into()),
127            SafeByResult::Unknown => Err(ErrorKind::NoData.into()),
128        }
129    }
130}
131
132#[derive(cynic::InlineFragments, Debug)]
133pub enum CalculateModuleAddressResult {
134    ModuleAddress(ModuleAddress),
135    InvalidAddressError(InvalidAddressError),
136    QueryFailedError(QueryFailedError),
137    #[cynic(fallback)]
138    Unknown,
139}
140
141impl From<CalculateModuleAddressResult> for Result<ChainAddress, BlokliClientError> {
142    fn from(value: CalculateModuleAddressResult) -> Self {
143        match value {
144            CalculateModuleAddressResult::ModuleAddress(address) => {
145                let address = address.module_address.to_lowercase();
146                Ok(hex::decode(address.trim_start_matches("0x"))
147                    .map_err(|_| BlokliClientError::from(ErrorKind::ParseError))
148                    .and_then(|bytes| bytes.try_into().map_err(|_| ErrorKind::ParseError.into()))?)
149            }
150            CalculateModuleAddressResult::InvalidAddressError(e) => Err(e.into()),
151            CalculateModuleAddressResult::QueryFailedError(e) => Err(e.into()),
152            CalculateModuleAddressResult::Unknown => Err(ErrorKind::NoData.into()),
153        }
154    }
155}