sui_rpc/client/
mod.rs

1use std::time::Duration;
2use tap::Pipe;
3use tonic::codec::CompressionEncoding;
4use tonic::transport::channel::ClientTlsConfig;
5
6mod response_ext;
7pub use response_ext::ResponseExt;
8
9mod interceptors;
10pub use interceptors::HeadersInterceptor;
11
12mod staking_rewards;
13pub use staking_rewards::DelegatedStake;
14
15mod coin_selection;
16mod lists;
17
18mod transaction_execution;
19pub use transaction_execution::ExecuteAndWaitError;
20
21use crate::proto::sui::rpc::v2::ledger_service_client::LedgerServiceClient;
22use crate::proto::sui::rpc::v2::move_package_service_client::MovePackageServiceClient;
23use crate::proto::sui::rpc::v2::signature_verification_service_client::SignatureVerificationServiceClient;
24use crate::proto::sui::rpc::v2::state_service_client::StateServiceClient;
25use crate::proto::sui::rpc::v2::subscription_service_client::SubscriptionServiceClient;
26use crate::proto::sui::rpc::v2::transaction_execution_service_client::TransactionExecutionServiceClient;
27
28type Result<T, E = tonic::Status> = std::result::Result<T, E>;
29type BoxError = Box<dyn std::error::Error + Send + Sync + 'static>;
30type Channel<'a> = tonic::service::interceptor::InterceptedService<
31    &'a mut tonic::transport::Channel,
32    &'a HeadersInterceptor,
33>;
34
35#[derive(Clone)]
36pub struct Client {
37    uri: http::Uri,
38    channel: tonic::transport::Channel,
39    headers: HeadersInterceptor,
40    max_decoding_message_size: Option<usize>,
41}
42
43impl Client {
44    /// URL for the public-good, Sui Foundation provided fullnodes for mainnet.
45    pub const MAINNET_FULLNODE: &str = "https://fullnode.mainnet.sui.io";
46
47    /// URL for the public-good, Sui Foundation provided fullnodes for testnet.
48    pub const TESTNET_FULLNODE: &str = "https://fullnode.testnet.sui.io";
49
50    /// URL for the public-good, Sui Foundation provided fullnodes for devnet.
51    pub const DEVNET_FULLNODE: &str = "https://fullnode.devnet.sui.io";
52
53    /// URL for the public-good, Sui Foundation provided archive for mainnet.
54    pub const MAINNET_ARCHIVE: &str = "https://archive.mainnet.sui.io";
55
56    /// URL for the public-good, Sui Foundation provided archive for testnet.
57    pub const TESTNET_ARCHIVE: &str = "https://archive.testnet.sui.io";
58
59    #[allow(clippy::result_large_err)]
60    pub fn new<T>(uri: T) -> Result<Self>
61    where
62        T: TryInto<http::Uri>,
63        T::Error: Into<BoxError>,
64    {
65        let uri = uri
66            .try_into()
67            .map_err(Into::into)
68            .map_err(tonic::Status::from_error)?;
69        let mut endpoint = tonic::transport::Endpoint::from(uri.clone());
70        if uri.scheme() == Some(&http::uri::Scheme::HTTPS) {
71            endpoint = endpoint
72                .tls_config(ClientTlsConfig::new().with_enabled_roots())
73                .map_err(Into::into)
74                .map_err(tonic::Status::from_error)?;
75        }
76        let channel = endpoint
77            .connect_timeout(Duration::from_secs(5))
78            .http2_keep_alive_interval(Duration::from_secs(5))
79            .connect_lazy();
80
81        Ok(Self {
82            uri,
83            channel,
84            headers: Default::default(),
85            max_decoding_message_size: None,
86        })
87    }
88
89    pub fn with_headers(mut self, headers: HeadersInterceptor) -> Self {
90        self.headers = headers;
91        self
92    }
93
94    pub fn with_max_decoding_message_size(mut self, limit: usize) -> Self {
95        self.max_decoding_message_size = Some(limit);
96        self
97    }
98
99    pub fn uri(&self) -> &http::Uri {
100        &self.uri
101    }
102
103    pub fn ledger_client(&mut self) -> LedgerServiceClient<Channel<'_>> {
104        LedgerServiceClient::with_interceptor(&mut self.channel, &self.headers)
105            .accept_compressed(CompressionEncoding::Zstd)
106            .pipe(|client| {
107                if let Some(limit) = self.max_decoding_message_size {
108                    client.max_decoding_message_size(limit)
109                } else {
110                    client
111                }
112            })
113    }
114
115    pub fn state_client(&mut self) -> StateServiceClient<Channel<'_>> {
116        StateServiceClient::with_interceptor(&mut self.channel, &self.headers)
117            .accept_compressed(CompressionEncoding::Zstd)
118            .pipe(|client| {
119                if let Some(limit) = self.max_decoding_message_size {
120                    client.max_decoding_message_size(limit)
121                } else {
122                    client
123                }
124            })
125    }
126
127    pub fn execution_client(&mut self) -> TransactionExecutionServiceClient<Channel<'_>> {
128        TransactionExecutionServiceClient::with_interceptor(&mut self.channel, &self.headers)
129            .accept_compressed(CompressionEncoding::Zstd)
130            .pipe(|client| {
131                if let Some(limit) = self.max_decoding_message_size {
132                    client.max_decoding_message_size(limit)
133                } else {
134                    client
135                }
136            })
137    }
138
139    pub fn package_client(&mut self) -> MovePackageServiceClient<Channel<'_>> {
140        MovePackageServiceClient::with_interceptor(&mut self.channel, &self.headers)
141            .accept_compressed(CompressionEncoding::Zstd)
142            .pipe(|client| {
143                if let Some(limit) = self.max_decoding_message_size {
144                    client.max_decoding_message_size(limit)
145                } else {
146                    client
147                }
148            })
149    }
150
151    pub fn signature_verification_client(
152        &mut self,
153    ) -> SignatureVerificationServiceClient<Channel<'_>> {
154        SignatureVerificationServiceClient::with_interceptor(&mut self.channel, &self.headers)
155            .accept_compressed(CompressionEncoding::Zstd)
156            .pipe(|client| {
157                if let Some(limit) = self.max_decoding_message_size {
158                    client.max_decoding_message_size(limit)
159                } else {
160                    client
161                }
162            })
163    }
164
165    pub fn subscription_client(&mut self) -> SubscriptionServiceClient<Channel<'_>> {
166        SubscriptionServiceClient::with_interceptor(&mut self.channel, &self.headers)
167            .accept_compressed(CompressionEncoding::Zstd)
168            .pipe(|client| {
169                if let Some(limit) = self.max_decoding_message_size {
170                    client.max_decoding_message_size(limit)
171                } else {
172                    client
173                }
174            })
175    }
176}