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 pub const MAINNET_FULLNODE: &str = "https://fullnode.mainnet.sui.io";
46
47 pub const TESTNET_FULLNODE: &str = "https://fullnode.testnet.sui.io";
49
50 pub const DEVNET_FULLNODE: &str = "https://fullnode.devnet.sui.io";
52
53 pub const MAINNET_ARCHIVE: &str = "https://archive.mainnet.sui.io";
55
56 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}