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