surfpool_core/rpc/bank_data.rs
1use jsonrpc_core::Result;
2use jsonrpc_derive::rpc;
3use solana_client::{
4 rpc_config::{RpcBlockProductionConfig, RpcContextConfig},
5 rpc_custom_error::RpcCustomError,
6 rpc_response::{
7 RpcBlockProduction, RpcInflationGovernor, RpcInflationRate, RpcResponseContext,
8 },
9};
10use solana_clock::Slot;
11use solana_commitment_config::CommitmentConfig;
12use solana_epoch_schedule::EpochSchedule;
13use solana_rpc_client_api::response::Response as RpcResponse;
14
15use super::{RunloopContext, State};
16use crate::surfnet::SURFPOOL_IDENTITY_PUBKEY;
17
18#[rpc]
19pub trait BankData {
20 type Metadata;
21
22 /// Returns the minimum balance required for rent exemption based on the given data length.
23 ///
24 /// This RPC method calculates the minimum balance required for an account to be exempt from
25 /// rent charges. It uses the data length of the account to determine the balance. The result
26 /// can help users manage their accounts by ensuring they have enough balance to cover rent
27 /// exemption, preventing accounts from being purged by the system.
28 ///
29 /// ## Parameters
30 /// - `data_len`: The length (in bytes) of the account data. This is used to determine the
31 /// minimum balance required for rent exemption.
32 /// - `commitment`: (Optional) A `CommitmentConfig` that allows specifying the level of
33 /// commitment for querying. If not provided, the default commitment level will be used.
34 ///
35 /// ## Returns
36 /// - `Result<u64>`: The method returns the minimum balance required for rent exemption
37 /// as a `u64`. If successful, it will be wrapped in `Ok`, otherwise an error will be
38 /// returned.
39 ///
40 /// ## Example Request (JSON-RPC)
41 /// ```json
42 /// {
43 /// "jsonrpc": "2.0",
44 /// "id": 1,
45 /// "method": "getMinimumBalanceForRentExemption",
46 /// "params": [128]
47 /// }
48 /// ```
49 ///
50 /// ## Example Response
51 /// ```json
52 /// {
53 /// "jsonrpc": "2.0",
54 /// "result": 2039280,
55 /// "id": 1
56 /// }
57 /// ```
58 ///
59 /// # Notes
60 /// - This method is commonly used to determine the required balance when creating new accounts
61 /// or performing account setup operations that need rent exemption.
62 /// - The `commitment` parameter allows users to specify the level of assurance they want
63 /// regarding the state of the ledger. For example, using `Confirmed` or `Finalized` ensures
64 /// that the state is more reliable.
65 ///
66 /// ## Errors
67 /// - If there is an issue with the `data_len` or `commitment` parameter (e.g., invalid data),
68 /// an error will be returned.
69 #[rpc(meta, name = "getMinimumBalanceForRentExemption")]
70 fn get_minimum_balance_for_rent_exemption(
71 &self,
72 meta: Self::Metadata,
73 data_len: usize,
74 commitment: Option<CommitmentConfig>,
75 ) -> Result<u64>;
76
77 /// Retrieves the inflation governor settings for the network.
78 ///
79 /// This RPC method returns the current inflation governor configuration, which controls the
80 /// inflation rate of the network. The inflation governor is responsible for adjusting the
81 /// inflation rate over time, with parameters like the initial and terminal inflation rates,
82 /// the taper rate, the foundation amount, and the foundation term.
83 ///
84 /// ## Parameters
85 /// - `commitment`: (Optional) A `CommitmentConfig` that specifies the commitment level for
86 /// querying the inflation governor settings. If not provided, the default commitment level
87 /// is used. Valid commitment levels include `Processed`, `Confirmed`, or `Finalized`.
88 ///
89 /// ## Returns
90 /// - `Result<RpcInflationGovernor>`: The method returns an `RpcInflationGovernor` struct that
91 /// contains the inflation parameters if successful. Otherwise, an error will be returned.
92 ///
93 /// ## Example Request (JSON-RPC)
94 /// ```json
95 /// {
96 /// "jsonrpc": "2.0",
97 /// "id": 1,
98 /// "method": "getInflationGovernor",
99 /// "params": []
100 /// }
101 /// ```
102 ///
103 /// ## Example Response
104 /// ```json
105 /// {
106 /// "jsonrpc": "2.0",
107 /// "result": {
108 /// "initial": 0.15,
109 /// "terminal": 0.05,
110 /// "taper": 0.9,
111 /// "foundation": 0.02,
112 /// "foundation_term": 5.0
113 /// },
114 /// "id": 1
115 /// }
116 /// ```
117 ///
118 /// # Notes
119 /// - The inflation governor defines how inflation changes over time, ensuring the network's
120 /// growth remains stable and sustainable.
121 /// - The `commitment` parameter allows users to define how strongly they want to ensure the
122 /// inflation data is confirmed or finalized when queried. For example, using `Confirmed` or
123 /// `Finalized` ensures a more reliable inflation state.
124 ///
125 /// ## Errors
126 /// - If there is an issue with the `commitment` parameter or an internal error occurs,
127 /// an error will be returned.
128 #[rpc(meta, name = "getInflationGovernor")]
129 fn get_inflation_governor(
130 &self,
131 meta: Self::Metadata,
132 commitment: Option<CommitmentConfig>,
133 ) -> Result<RpcInflationGovernor>;
134
135 /// Retrieves the current inflation rate for the network.
136 ///
137 /// This RPC method returns the current inflation rate, including the breakdown of inflation
138 /// allocated to different entities such as validators and the foundation, along with the current
139 /// epoch during which the rate applies.
140 ///
141 /// ## Parameters
142 /// - No parameters are required for this method.
143 ///
144 /// ## Returns
145 /// - `Result<RpcInflationRate>`: The method returns an `RpcInflationRate` struct that contains
146 /// the total inflation rate, the validator portion, the foundation portion, and the epoch
147 /// during which this inflation rate applies.
148 ///
149 /// ## Example Request (JSON-RPC)
150 /// ```json
151 /// {
152 /// "jsonrpc": "2.0",
153 /// "id": 1,
154 /// "method": "getInflationRate",
155 /// "params": []
156 /// }
157 /// ```
158 ///
159 /// ## Example Response
160 /// ```json
161 /// {
162 /// "jsonrpc": "2.0",
163 /// "result": {
164 /// "total": 0.10,
165 /// "validator": 0.07,
166 /// "foundation": 0.03,
167 /// "epoch": 1500
168 /// },
169 /// "id": 1
170 /// }
171 /// ```
172 ///
173 /// # Notes
174 /// - The total inflation rate is distributed among validators and the foundation based on
175 /// the configuration defined in the inflation governor.
176 /// - The epoch field indicates the current epoch number during which this inflation rate applies.
177 /// An epoch is a period during which the network operates under certain parameters.
178 /// - Inflation rates can change over time depending on network conditions and governance decisions.
179 ///
180 /// ## Errors
181 /// - If there is an internal error, or if the RPC request is malformed, an error will be returned.
182 #[rpc(meta, name = "getInflationRate")]
183 fn get_inflation_rate(&self, meta: Self::Metadata) -> Result<RpcInflationRate>;
184
185 /// Retrieves the epoch schedule for the network.
186 ///
187 /// This RPC method returns the configuration for the network's epoch schedule, including
188 /// details on the number of slots per epoch, leader schedule offsets, and epoch warmup.
189 ///
190 /// ## Parameters
191 /// - No parameters are required for this method.
192 ///
193 /// ## Returns
194 /// - `Result<EpochSchedule>`: The method returns an `EpochSchedule` struct, which contains
195 /// information about the slots per epoch, leader schedule offsets, warmup state, and the
196 /// first epoch after the warmup period.
197 ///
198 /// ## Example Request (JSON-RPC)
199 /// ```json
200 /// {
201 /// "jsonrpc": "2.0",
202 /// "id": 1,
203 /// "method": "getEpochSchedule",
204 /// "params": []
205 /// }
206 /// ```
207 ///
208 /// ## Example Response
209 /// ```json
210 /// {
211 /// "jsonrpc": "2.0",
212 /// "result": {
213 /// "slotsPerEpoch": 432000,
214 /// "leaderScheduleSlotOffset": 500,
215 /// "warmup": true,
216 /// "firstNormalEpoch": 8,
217 /// "firstNormalSlot": 1073741824
218 /// },
219 /// "id": 1
220 /// }
221 /// ```
222 ///
223 /// # Notes
224 /// - The `slots_per_epoch` defines the maximum number of slots in each epoch, which determines
225 /// the number of time slots available for network validators to produce blocks.
226 /// - The `leader_schedule_slot_offset` specifies how many slots before an epoch’s start the leader
227 /// schedule calculation begins for that epoch.
228 /// - The `warmup` field indicates whether the epochs start short and grow over time.
229 /// - The `first_normal_epoch` marks the first epoch after the warmup period.
230 /// - The `first_normal_slot` gives the first slot after the warmup period in terms of the number of slots
231 /// from the start of the network.
232 ///
233 /// ## Errors
234 /// - If the RPC request is malformed, or if there is an internal error, an error will be returned.
235 #[rpc(meta, name = "getEpochSchedule")]
236 fn get_epoch_schedule(&self, meta: Self::Metadata) -> Result<EpochSchedule>;
237
238 /// Retrieves the leader of the current slot.
239 ///
240 /// This RPC method returns the leader for the current slot in the Solana network. The leader is responsible
241 /// for producing blocks for the current slot. The leader is selected based on the Solana consensus mechanism.
242 ///
243 /// ## Parameters
244 /// - `config`: An optional configuration for the request, which can include:
245 /// - `commitment`: A commitment level that defines how "final" the data must be.
246 /// - `min_context_slot`: An optional parameter to specify a minimum slot for the request.
247 ///
248 /// ## Returns
249 /// - `Result<String>`: The method returns a `String` representing the public key of the leader for the current slot.
250 ///
251 /// ## Example Request (JSON-RPC)
252 /// ```json
253 /// {
254 /// "jsonrpc": "2.0",
255 /// "id": 1,
256 /// "method": "getSlotLeader",
257 /// "params": []
258 /// }
259 /// ```
260 ///
261 /// ## Example Response
262 /// ```json
263 /// {
264 /// "jsonrpc": "2.0",
265 /// "result": "3HgA9r8H9z5Pb2L6Pt5Yq1QoFwgr6YwdKKUh9n2ANp5U"
266 /// }
267 /// ```
268 ///
269 /// # Notes
270 /// - The leader for a given slot is selected based on the Solana network's consensus mechanism, and this method
271 /// allows you to query the current leader.
272 #[rpc(meta, name = "getSlotLeader")]
273 fn get_slot_leader(
274 &self,
275 meta: Self::Metadata,
276 config: Option<RpcContextConfig>,
277 ) -> Result<String>;
278
279 /// Retrieves the leaders for a specified range of slots.
280 ///
281 /// This RPC method returns the leaders for a specified range of slots in the Solana network. You can
282 /// specify the `start_slot` from which the leaders should be queried and limit the number of results
283 /// with the `limit` parameter. The leaders are responsible for producing blocks in the respective slots.
284 ///
285 /// ## Parameters
286 /// - `start_slot`: The starting slot number for which the leaders should be queried.
287 /// - `limit`: The number of slots (starting from `start_slot`) for which the leaders should be retrieved.
288 ///
289 /// ## Returns
290 /// - `Result<Vec<String>>`: A vector of `String` values representing the public keys of the leaders for
291 /// the specified slot range.
292 ///
293 /// ## Example Request (JSON-RPC)
294 /// ```json
295 /// {
296 /// "jsonrpc": "2.0",
297 /// "id": 1,
298 /// "method": "getSlotLeaders",
299 /// "params": [1000, 5]
300 /// }
301 /// ```
302 ///
303 /// ## Example Response
304 /// ```json
305 /// {
306 /// "jsonrpc": "2.0",
307 /// "result": [
308 /// "3HgA9r8H9z5Pb2L6Pt5Yq1QoFwgr6YwdKKUh9n2ANp5U",
309 /// "BBh1FwXts8EZY6rPZ5kS2ygq99wYjFd5K5daRjc7eF9X",
310 /// "4XYo7yP5J2J8sLNSW3wGYPk3mdS1rbZUy4oFCp7wH1DN",
311 /// "8v1Cp6sHZh8XfGWS7sHZczH3v9NxdgMbo3g91Sh88dcJ",
312 /// "N6bPqwEoD9StS4AnzE27rHyz47tPcsZQjvW9w8p2NhF7"
313 /// ]
314 /// }
315 /// ```
316 ///
317 /// # Notes
318 /// - The leaders are returned in the order corresponding to the slots queried, starting from `start_slot`
319 /// and continuing for `limit` slots.
320 /// - This method provides an efficient way to get multiple leaders for a range of slots, useful for tracking
321 /// leaders over time or for scheduling purposes in decentralized applications.
322 #[rpc(meta, name = "getSlotLeaders")]
323 fn get_slot_leaders(
324 &self,
325 meta: Self::Metadata,
326 start_slot: Slot,
327 limit: u64,
328 ) -> Result<Vec<String>>;
329
330 /// Retrieves block production information for the specified validator identity or range of slots.
331 ///
332 /// This RPC method returns block production details for a given validator identity or a range of slots
333 /// within a certain epoch. If no `identity` is provided, the method returns block production data for all
334 /// validators. If a `range` is provided, it will return block production information for the slots within
335 /// the specified range.
336 ///
337 /// ## Parameters
338 /// - `config`: An optional configuration object that can include:
339 /// - `identity`: The base-58 encoded public key of a validator to query for block production data. If `None`, results for all validators will be returned.
340 /// - `range`: A range of slots for which block production information is needed. The range will default to the current epoch if `None`.
341 /// - `commitment`: The commitment level (optional) to use when querying for the block production data.
342 ///
343 /// ## Returns
344 /// - `Result<RpcResponse<RpcBlockProduction>>`: The result contains a response object with block production data, including the number of leader slots and blocks produced by each validator.
345 ///
346 /// ## Example Request (JSON-RPC)
347 /// ```json
348 /// {
349 /// "jsonrpc": "2.0",
350 /// "id": 1,
351 /// "method": "getBlockProduction",
352 /// "params": [{
353 /// "identity": "3HgA9r8H9z5Pb2L6Pt5Yq1QoFwgr6YwdKKUh9n2ANp5U",
354 /// "range": {
355 /// "firstSlot": 1000,
356 /// "lastSlot": 1050
357 /// }
358 /// }]
359 /// }
360 /// ```
361 ///
362 /// ## Example Response
363 /// ```json
364 /// {
365 /// "jsonrpc": "2.0",
366 /// "result": {
367 /// "byIdentity": {
368 /// "3HgA9r8H9z5Pb2L6Pt5Yq1QoFwgr6YwdKKUh9n2ANp5U": [10, 8],
369 /// "BBh1FwXts8EZY6rPZ5kS2ygq99wYjFd5K5daRjc7eF9X": [5, 4]
370 /// },
371 /// "range": {
372 /// "firstSlot": 1000,
373 /// "lastSlot": 1050
374 /// }
375 /// }
376 /// }
377 /// ```
378 ///
379 /// # Notes
380 /// - The response contains a map of validator identities to a tuple of two values:
381 /// - The first value is the number of leader slots.
382 /// - The second value is the number of blocks produced by that validator in the queried range.
383 /// - The `range` object specifies the range of slots that the block production information applies to, with `first_slot` being the starting slot and `last_slot` being the optional ending slot.
384 ///
385 /// ## Example Response Interpretation
386 /// - In the example response, the identity `3HgA9r8H9z5Pb2L6Pt5Yq1QoFwgr6YwdKKUh9n2ANp5U` produced 10 leader slots and 8 blocks between slots 1000 and 1050.
387 /// - Similarly, `BBh1FwXts8EZY6rPZ5kS2ygq99wYjFd5K5daRjc7eF9X` produced 5 leader slots and 4 blocks in the same slot range.
388 #[rpc(meta, name = "getBlockProduction")]
389 fn get_block_production(
390 &self,
391 meta: Self::Metadata,
392 config: Option<RpcBlockProductionConfig>,
393 ) -> Result<RpcResponse<RpcBlockProduction>>;
394}
395
396#[derive(Clone)]
397pub struct SurfpoolBankDataRpc;
398impl BankData for SurfpoolBankDataRpc {
399 type Metadata = Option<RunloopContext>;
400
401 fn get_minimum_balance_for_rent_exemption(
402 &self,
403 meta: Self::Metadata,
404 data_len: usize,
405 _commitment: Option<CommitmentConfig>,
406 ) -> Result<u64> {
407 meta.with_svm_reader(move |svm_reader| {
408 svm_reader
409 .inner
410 .minimum_balance_for_rent_exemption(data_len)
411 })
412 .map_err(Into::into)
413 }
414
415 fn get_inflation_governor(
416 &self,
417 meta: Self::Metadata,
418 _commitment: Option<CommitmentConfig>,
419 ) -> Result<RpcInflationGovernor> {
420 meta.with_svm_reader(|svm_reader| svm_reader.inflation.into())
421 .map_err(Into::into)
422 }
423
424 fn get_inflation_rate(&self, meta: Self::Metadata) -> Result<RpcInflationRate> {
425 meta.with_svm_reader(|svm_reader| {
426 let inflation_activation_slot =
427 svm_reader.blocks.keys().min().copied().unwrap_or_default();
428 let epoch_schedule = svm_reader.inner.get_sysvar::<EpochSchedule>();
429 let inflation_start_slot = epoch_schedule.get_first_slot_in_epoch(
430 epoch_schedule
431 .get_epoch(inflation_activation_slot)
432 .saturating_sub(1),
433 );
434 let epoch = svm_reader.latest_epoch_info().epoch;
435 let num_slots = epoch_schedule.get_first_slot_in_epoch(epoch) - inflation_start_slot;
436
437 let inflation = svm_reader.inflation;
438 let slots_per_year = svm_reader.genesis_config.slots_per_year();
439
440 let slot_in_year = num_slots as f64 / slots_per_year;
441
442 RpcInflationRate {
443 total: inflation.total(slot_in_year),
444 validator: inflation.validator(slot_in_year),
445 foundation: inflation.foundation(slot_in_year),
446 epoch,
447 }
448 })
449 .map_err(Into::into)
450 }
451
452 fn get_epoch_schedule(&self, meta: Self::Metadata) -> Result<EpochSchedule> {
453 meta.with_svm_reader(move |svm_reader| svm_reader.inner.get_sysvar::<EpochSchedule>())
454 .map_err(Into::into)
455 }
456
457 fn get_slot_leader(
458 &self,
459 meta: Self::Metadata,
460 config: Option<RpcContextConfig>,
461 ) -> Result<String> {
462 let svm_locker = meta.get_svm_locker()?;
463 let config = config.unwrap_or_default();
464
465 let committed_slot =
466 svm_locker.get_slot_for_commitment(&config.commitment.unwrap_or_default());
467
468 // validate minContextSlot if provided
469 if let Some(min_context_slot) = config.min_context_slot {
470 if committed_slot < min_context_slot {
471 return Err(RpcCustomError::MinContextSlotNotReached {
472 context_slot: min_context_slot,
473 }
474 .into());
475 }
476 }
477
478 Ok(SURFPOOL_IDENTITY_PUBKEY.to_string())
479 }
480
481 fn get_slot_leaders(
482 &self,
483 meta: Self::Metadata,
484 start_slot: Slot,
485 limit: u64,
486 ) -> Result<Vec<String>> {
487 if limit == 0 || limit > 5000 {
488 return Err(jsonrpc_core::Error {
489 code: jsonrpc_core::ErrorCode::InvalidParams,
490 message: "Limit must be between 1 and 5000".to_string(),
491 data: None,
492 });
493 }
494
495 let svm_locker = meta.get_svm_locker()?;
496 let epoch_info = svm_locker.get_epoch_info();
497
498 let first_slot_in_epoch = epoch_info
499 .absolute_slot
500 .saturating_sub(epoch_info.slot_index);
501 let last_slot_in_epoch = first_slot_in_epoch + epoch_info.slots_in_epoch.saturating_sub(1);
502 if start_slot > last_slot_in_epoch || (start_slot + limit) > last_slot_in_epoch {
503 return Err(jsonrpc_core::Error {
504 code: jsonrpc_core::ErrorCode::InvalidParams,
505 message: format!(
506 "Invalid slot range: leader schedule for epoch {} is unavailable",
507 epoch_info.epoch
508 ),
509 data: None,
510 });
511 }
512
513 Ok(vec![])
514 }
515
516 fn get_block_production(
517 &self,
518 meta: Self::Metadata,
519 config: Option<RpcBlockProductionConfig>,
520 ) -> Result<RpcResponse<RpcBlockProduction>> {
521 meta.with_svm_reader(|svm_reader| {
522 let current_slot = svm_reader.get_latest_absolute_slot();
523 let epoch_info = &svm_reader.latest_epoch_info;
524
525 let (first_slot, last_slot) = if let Some(ref config) = config {
526 if let Some(ref range) = config.range {
527 (range.first_slot, range.last_slot.unwrap_or(current_slot))
528 } else {
529 let epoch_start_slot = epoch_info.absolute_slot - epoch_info.slot_index;
530 (epoch_start_slot, current_slot)
531 }
532 } else {
533 let epoch_start_slot = epoch_info.absolute_slot - epoch_info.slot_index;
534 (epoch_start_slot, current_slot)
535 };
536
537 RpcResponse {
538 context: RpcResponseContext::new(current_slot),
539 value: RpcBlockProduction {
540 // Empty HashMap - no validator block production data in simulation
541 by_identity: std::collections::HashMap::new(),
542 range: solana_client::rpc_response::RpcBlockProductionRange {
543 first_slot,
544 last_slot,
545 },
546 },
547 }
548 })
549 .map_err(Into::into)
550 }
551}
552
553#[cfg(test)]
554mod tests {
555 use solana_client::rpc_config::RpcBlockProductionConfigRange;
556 use solana_commitment_config::CommitmentLevel;
557 use solana_sdk::inflation::Inflation;
558
559 use super::*;
560 use crate::tests::helpers::TestSetup;
561
562 #[tokio::test(flavor = "multi_thread")]
563 async fn test_get_epoch_schedule() {
564 let setup = TestSetup::new(SurfpoolBankDataRpc);
565 let res = setup.rpc.get_epoch_schedule(Some(setup.context)).unwrap();
566
567 assert_eq!(res, EpochSchedule::default());
568 }
569
570 #[test]
571 fn test_get_block_production() {
572 let setup = TestSetup::new(SurfpoolBankDataRpc);
573
574 // test with no config
575 let result = setup
576 .rpc
577 .get_block_production(Some(setup.context.clone()), None)
578 .unwrap();
579
580 // verify empty results (simulation mode)
581 assert!(
582 result.value.by_identity.is_empty(),
583 "Should have no validators in simulation"
584 );
585 assert!(
586 result.value.range.first_slot <= result.value.range.last_slot,
587 "Valid slot range"
588 );
589
590 // test with custom range
591 let config = Some(RpcBlockProductionConfig {
592 identity: None,
593 range: Some(RpcBlockProductionConfigRange {
594 first_slot: 100,
595 last_slot: Some(200),
596 }),
597 commitment: None,
598 });
599
600 let result2 = setup
601 .rpc
602 .get_block_production(Some(setup.context), config)
603 .unwrap();
604
605 assert_eq!(result2.value.range.first_slot, 100);
606 assert_eq!(result2.value.range.last_slot, 200);
607 assert!(result2.value.by_identity.is_empty());
608 }
609
610 #[test]
611 fn test_get_slot_leaders() {
612 let setup = TestSetup::new(SurfpoolBankDataRpc);
613
614 // test with valid parameters
615 let result = setup
616 .rpc
617 .get_slot_leaders(Some(setup.context.clone()), 0, 10)
618 .unwrap();
619
620 assert!(
621 result.is_empty(),
622 "Should return empty leaders in simulation"
623 );
624
625 // test with invalid limit
626 let err = setup
627 .rpc
628 .get_slot_leaders(Some(setup.context.clone()), 0, 6000)
629 .unwrap_err();
630
631 assert_eq!(
632 err.code,
633 jsonrpc_core::ErrorCode::InvalidParams,
634 "Should return InvalidParams error for limit > 5000"
635 );
636
637 let latest_slot = setup.context.svm_locker.get_latest_absolute_slot();
638
639 // test with start_slot >= latest_slot
640 let err = setup
641 .rpc
642 .get_slot_leaders(Some(setup.context), latest_slot + 100, 10)
643 .unwrap_err();
644
645 assert_eq!(
646 err.code,
647 jsonrpc_core::ErrorCode::InvalidParams,
648 "Should return InvalidParams error for start_slot >= latest_slot"
649 );
650 }
651
652 #[test]
653 fn test_get_inflation_rate() {
654 let setup = TestSetup::new(SurfpoolBankDataRpc);
655 let result = setup.rpc.get_inflation_rate(Some(setup.context));
656 assert!(result.is_ok())
657 }
658
659 #[test]
660 fn test_get_inflation_governor() {
661 let setup = TestSetup::new(SurfpoolBankDataRpc);
662
663 let result = setup
664 .rpc
665 .get_inflation_governor(Some(setup.context), None)
666 .unwrap();
667
668 assert_eq!(result, Inflation::default().into());
669 }
670
671 #[test]
672 fn test_get_minimum_balance_for_rent_exemption() {
673 let setup = TestSetup::new(SurfpoolBankDataRpc);
674 let rent = setup
675 .rpc
676 .get_minimum_balance_for_rent_exemption(Some(setup.context), 0, None)
677 .unwrap();
678
679 assert_eq!(rent, 890880)
680 }
681
682 #[tokio::test(flavor = "multi_thread")]
683 async fn test_get_slot_leader_basic() {
684 let setup = TestSetup::new(SurfpoolBankDataRpc);
685
686 let result = setup.rpc.get_slot_leader(Some(setup.context.clone()), None);
687
688 match result {
689 Ok(identity) => {
690 assert_eq!(identity, SURFPOOL_IDENTITY_PUBKEY.to_string());
691 println!("✅ Basic test passed");
692 }
693 Err(e) => {
694 panic!("❌ Test failed: {:?}", e);
695 }
696 }
697 }
698
699 #[tokio::test(flavor = "multi_thread")]
700 async fn test_get_slot_leader_with_config() {
701 let setup = TestSetup::new(SurfpoolBankDataRpc);
702
703 let config = RpcContextConfig {
704 commitment: Some(CommitmentConfig {
705 commitment: CommitmentLevel::Processed,
706 }),
707 min_context_slot: None,
708 };
709
710 let result = setup
711 .rpc
712 .get_slot_leader(Some(setup.context.clone()), Some(config));
713
714 match result {
715 Ok(identity) => {
716 assert_eq!(identity, SURFPOOL_IDENTITY_PUBKEY.to_string());
717 println!("✅ Config test passed");
718 }
719 Err(e) => {
720 panic!("❌ Test failed: {:?}", e);
721 }
722 }
723 }
724
725 #[tokio::test(flavor = "multi_thread")]
726 async fn test_get_slot_leader_min_context_slot_error() {
727 let setup = TestSetup::new(SurfpoolBankDataRpc);
728
729 let config = RpcContextConfig {
730 commitment: Some(CommitmentConfig {
731 commitment: CommitmentLevel::Finalized,
732 }),
733 min_context_slot: Some(999999), // high number that should fail
734 };
735
736 let result = setup
737 .rpc
738 .get_slot_leader(Some(setup.context.clone()), Some(config));
739
740 assert!(result.is_err());
741 println!("✅ MinContextSlot error test passed");
742 }
743}