1use starknet_core::error::{ContractExecutionError, Error, StateError};
2use starknet_rs_core::types::{BlockId as ImportedBlockId, Felt, MsgFromL1};
3use starknet_rs_providers::Provider;
4use starknet_types::contract_address::ContractAddress;
5use starknet_types::felt::{ClassHash, TransactionHash};
6use starknet_types::patricia_key::PatriciaKey;
7use starknet_types::rpc::block::{
8 Block, BlockHeader, BlockId, BlockResult, BlockStatus, BlockTag, PreConfirmedBlock,
9 PreConfirmedBlockHeader,
10};
11use starknet_types::rpc::state::StateUpdateResult;
12use starknet_types::rpc::transaction_receipt::FeeUnit;
13use starknet_types::rpc::transactions::{
14 BroadcastedTransaction, EventFilter, EventsChunk, FunctionCall, SimulationFlag, Transactions,
15};
16
17use super::error::{ApiError, StrictRpcResult};
18use super::models::{
19 BlockHashAndNumberOutput, GetStorageProofInput, L1TransactionHashInput, SyncingOutput,
20};
21use crate::api::account_helpers::{
22 BalanceQuery, PredeployedAccountsQuery, get_balance, get_balance_unit,
23 get_erc20_fee_unit_address,
24};
25use crate::api::models::{
26 AccountBalanceResponse, AccountBalancesResponse, DevnetResponse, SerializableAccount,
27 StarknetResponse,
28};
29use crate::api::{JsonRpcHandler, RPC_SPEC_VERSION};
30use crate::config::DevnetConfig;
31
32const DEFAULT_CONTINUATION_TOKEN: &str = "0";
33const CONTINUATION_TOKEN_ORIGIN_PREFIX: &str = "devnet-origin-";
34
35impl JsonRpcHandler {
37 pub fn spec_version(&self) -> StrictRpcResult {
39 Ok(StarknetResponse::String(RPC_SPEC_VERSION.to_string()).into())
40 }
41
42 pub async fn get_block_with_tx_hashes(&self, block_id: BlockId) -> StrictRpcResult {
44 let starknet = self.api.starknet.lock().await;
45
46 let block = starknet.get_block(&block_id).map_err(|err| match err {
47 Error::NoBlock => ApiError::BlockNotFound,
48 unknown_error => ApiError::StarknetDevnetError(unknown_error),
49 })?;
50
51 let transactions = Transactions::Hashes(block.get_transactions().to_owned());
52
53 Ok(match block.status() {
54 BlockStatus::PreConfirmed => StarknetResponse::PreConfirmedBlock(PreConfirmedBlock {
55 header: PreConfirmedBlockHeader::from(block),
56 transactions,
57 }),
58 _ => StarknetResponse::Block(Block {
59 status: *block.status(),
60 header: BlockHeader::from(block),
61 transactions,
62 }),
63 }
64 .into())
65 }
66
67 pub async fn get_block_with_txs(&self, block_id: BlockId) -> StrictRpcResult {
69 let block = self.api.starknet.lock().await.get_block_with_transactions(&block_id).map_err(
70 |err| match err {
71 Error::NoBlock => ApiError::BlockNotFound,
72 Error::NoTransaction => ApiError::TransactionNotFound,
73 unknown_error => ApiError::StarknetDevnetError(unknown_error),
74 },
75 )?;
76
77 match block {
78 BlockResult::Block(b) => Ok(StarknetResponse::Block(b).into()),
79 BlockResult::PreConfirmedBlock(b) => Ok(StarknetResponse::PreConfirmedBlock(b).into()),
80 }
81 }
82
83 pub async fn get_block_with_receipts(&self, block_id: BlockId) -> StrictRpcResult {
85 let block = self.api.starknet.lock().await.get_block_with_receipts(&block_id).map_err(
86 |e| match e {
87 Error::NoBlock => ApiError::BlockNotFound,
88 Error::NoTransaction => ApiError::TransactionNotFound,
89 unknown_error => ApiError::StarknetDevnetError(unknown_error),
90 },
91 )?;
92
93 match block {
94 BlockResult::Block(b) => Ok(StarknetResponse::Block(b).into()),
95 BlockResult::PreConfirmedBlock(b) => Ok(StarknetResponse::PreConfirmedBlock(b).into()),
96 }
97 }
98
99 pub async fn get_state_update(&self, block_id: BlockId) -> StrictRpcResult {
101 let state_update =
102 self.api.starknet.lock().await.block_state_update(&block_id).map_err(|e| match e {
103 Error::NoBlock => ApiError::BlockNotFound,
104 unknown_error => ApiError::StarknetDevnetError(unknown_error),
105 })?;
106
107 match state_update {
108 StateUpdateResult::StateUpdate(s) => Ok(StarknetResponse::StateUpdate(s).into()),
109 StateUpdateResult::PreConfirmedStateUpdate(s) => {
110 Ok(StarknetResponse::PreConfirmedStateUpdate(s).into())
111 }
112 }
113 }
114
115 pub async fn get_storage_at(
117 &self,
118 contract_address: ContractAddress,
119 key: PatriciaKey,
120 block_id: BlockId,
121 ) -> StrictRpcResult {
122 let felt = self
123 .api
124 .starknet
125 .lock()
126 .await
127 .contract_storage_at_block(&block_id, contract_address, key)
128 .map_err(|err| match err {
129 Error::NoBlock => ApiError::BlockNotFound,
130 Error::ContractNotFound | Error::StateError(StateError::NoneStorage(_)) => {
131 ApiError::ContractNotFound
132 }
133 e @ Error::NoStateAtBlock { .. } => ApiError::NoStateAtBlock { msg: e.to_string() },
134 unknown_error => ApiError::StarknetDevnetError(unknown_error),
135 })?;
136
137 Ok(StarknetResponse::Felt(felt).into())
138 }
139
140 pub async fn get_storage_proof(&self, data: GetStorageProofInput) -> StrictRpcResult {
142 match self.api.starknet.lock().await.get_block(&data.block_id) {
143 Ok(_) => Err(ApiError::StorageProofNotSupported),
145 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
146 Err(unknown_error) => Err(ApiError::StarknetDevnetError(unknown_error)),
147 }
148 }
149
150 pub async fn get_transaction_by_hash(
152 &self,
153 transaction_hash: TransactionHash,
154 ) -> StrictRpcResult {
155 match self.api.starknet.lock().await.get_transaction_by_hash(transaction_hash) {
156 Ok(transaction) => Ok(StarknetResponse::Transaction(transaction.clone()).into()),
157 Err(Error::NoTransaction) => Err(ApiError::TransactionNotFound),
158 Err(err) => Err(err.into()),
159 }
160 }
161
162 pub async fn get_transaction_status_by_hash(
164 &self,
165 transaction_hash: TransactionHash,
166 ) -> StrictRpcResult {
167 match self
168 .api
169 .starknet
170 .lock()
171 .await
172 .get_transaction_execution_and_finality_status(transaction_hash)
173 {
174 Ok(tx_status) => Ok(StarknetResponse::TransactionStatusByHash(tx_status).into()),
175 Err(Error::NoTransaction) => Err(ApiError::TransactionNotFound),
176 Err(err) => Err(err.into()),
177 }
178 }
179
180 pub async fn get_transaction_by_block_id_and_index(
182 &self,
183 block_id: BlockId,
184 index: u64,
185 ) -> StrictRpcResult {
186 match self.api.starknet.lock().await.get_transaction_by_block_id_and_index(&block_id, index)
187 {
188 Ok(transaction) => Ok(StarknetResponse::Transaction(transaction.clone()).into()),
189 Err(Error::InvalidTransactionIndexInBlock) => {
190 Err(ApiError::InvalidTransactionIndexInBlock)
191 }
192 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
193 Err(unknown_error) => Err(ApiError::StarknetDevnetError(unknown_error)),
194 }
195 }
196
197 pub async fn get_transaction_receipt_by_hash(
199 &self,
200 transaction_hash: TransactionHash,
201 ) -> StrictRpcResult {
202 match self.api.starknet.lock().await.get_transaction_receipt_by_hash(&transaction_hash) {
203 Ok(receipt) => {
204 Ok(StarknetResponse::TransactionReceiptByTransactionHash(Box::new(receipt)).into())
205 }
206 Err(Error::NoTransaction) => Err(ApiError::TransactionNotFound),
207 Err(err) => Err(err.into()),
208 }
209 }
210
211 pub async fn get_class(&self, block_id: BlockId, class_hash: ClassHash) -> StrictRpcResult {
213 match self.api.starknet.lock().await.get_class(&block_id, class_hash) {
214 Ok(contract_class) => {
215 Ok(StarknetResponse::ContractClass(contract_class.try_into()?).into())
216 }
217 Err(e) => Err(match e {
218 Error::NoBlock => ApiError::BlockNotFound,
219 Error::StateError(_) => ApiError::ClassHashNotFound,
220 e @ Error::NoStateAtBlock { .. } => ApiError::NoStateAtBlock { msg: e.to_string() },
221 unknown_error => ApiError::StarknetDevnetError(unknown_error),
222 }),
223 }
224 }
225
226 pub async fn get_compiled_casm(&self, class_hash: ClassHash) -> StrictRpcResult {
228 match self.api.starknet.lock().await.get_compiled_casm(class_hash) {
232 Ok(compiled_casm) => Ok(StarknetResponse::CompiledCasm(compiled_casm).into()),
233 Err(e) => Err(match e {
234 Error::NoBlock => ApiError::BlockNotFound,
235 Error::StateError(_) => ApiError::ClassHashNotFound,
236 e @ Error::NoStateAtBlock { .. } => ApiError::NoStateAtBlock { msg: e.to_string() },
237 unknown_error => ApiError::StarknetDevnetError(unknown_error),
238 }),
239 }
240 }
241
242 pub async fn get_class_at(
244 &self,
245 block_id: BlockId,
246 contract_address: ContractAddress,
247 ) -> StrictRpcResult {
248 match self.api.starknet.lock().await.get_class_at(&block_id, contract_address) {
249 Ok(contract_class) => {
250 Ok(StarknetResponse::ContractClass(contract_class.try_into()?).into())
251 }
252 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
253 Err(Error::StateError(StateError::NoneClassHash(_))) => {
254 Err(ApiError::ClassHashNotFound)
259 }
260 Err(Error::ContractNotFound | Error::StateError(_)) => Err(ApiError::ContractNotFound),
261 Err(e @ Error::NoStateAtBlock { .. }) => {
262 Err(ApiError::NoStateAtBlock { msg: e.to_string() })
263 }
264 Err(unknown_error) => Err(ApiError::StarknetDevnetError(unknown_error)),
265 }
266 }
267
268 pub async fn get_class_hash_at(
270 &self,
271 block_id: BlockId,
272 contract_address: ContractAddress,
273 ) -> StrictRpcResult {
274 match self.api.starknet.lock().await.get_class_hash_at(&block_id, contract_address) {
275 Ok(class_hash) => Ok(StarknetResponse::Felt(class_hash).into()),
276 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
277 Err(Error::ContractNotFound) => Err(ApiError::ContractNotFound),
278 Err(e @ Error::NoStateAtBlock { .. }) => {
279 Err(ApiError::NoStateAtBlock { msg: e.to_string() })
280 }
281 Err(unknown_error) => Err(ApiError::StarknetDevnetError(unknown_error)),
282 }
283 }
284
285 pub async fn get_block_txs_count(&self, block_id: BlockId) -> StrictRpcResult {
287 let num_trans_count = self.api.starknet.lock().await.get_block_txs_count(&block_id);
288 match num_trans_count {
289 Ok(count) => Ok(StarknetResponse::BlockTransactionCount(count).into()),
290 Err(_) => Err(ApiError::BlockNotFound),
291 }
292 }
293
294 pub async fn call(&self, block_id: BlockId, request: FunctionCall) -> StrictRpcResult {
296 match self.api.starknet.lock().await.call(
297 &block_id,
298 request.contract_address.into(),
299 request.entry_point_selector,
300 request.calldata,
301 ) {
302 Ok(result) => Ok(StarknetResponse::Call(result).into()),
303 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
304 Err(Error::ContractNotFound) => Err(ApiError::ContractNotFound),
305 Err(Error::EntrypointNotFound) => Err(ApiError::EntrypointNotFound),
306 Err(e @ Error::NoStateAtBlock { .. }) => {
307 Err(ApiError::NoStateAtBlock { msg: e.to_string() })
308 }
309 Err(Error::ContractExecutionError(execution_error)) => {
310 Err(ApiError::ContractError(execution_error))
311 }
312 Err(e) => Err(ApiError::ContractError(ContractExecutionError::Message(e.to_string()))),
313 }
314 }
315
316 pub async fn estimate_fee(
318 &self,
319 block_id: BlockId,
320 request: Vec<BroadcastedTransaction>,
321 simulation_flags: Vec<SimulationFlag>,
322 ) -> StrictRpcResult {
323 match self.api.starknet.lock().await.estimate_fee(&block_id, &request, &simulation_flags) {
324 Ok(result) => Ok(StarknetResponse::EstimateFee(result).into()),
325 Err(Error::ContractNotFound) => Err(ApiError::ContractNotFound),
326 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
327 Err(e @ Error::NoStateAtBlock { .. }) => {
328 Err(ApiError::NoStateAtBlock { msg: e.to_string() })
329 }
330 Err(Error::ContractExecutionErrorInSimulation { failure_index, execution_error }) => {
331 Err(ApiError::TransactionExecutionError { failure_index, execution_error })
332 }
333 Err(e) => Err(ApiError::ContractError(ContractExecutionError::from(e.to_string()))),
334 }
335 }
336
337 pub async fn estimate_message_fee(
338 &self,
339 block_id: &BlockId,
340 message: MsgFromL1,
341 ) -> StrictRpcResult {
342 match self.api.starknet.lock().await.estimate_message_fee(block_id, message) {
343 Ok(result) => Ok(StarknetResponse::EstimateMessageFee(result).into()),
344 Err(Error::ContractNotFound) => Err(ApiError::ContractNotFound),
345 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
346 Err(e @ Error::NoStateAtBlock { .. }) => {
347 Err(ApiError::NoStateAtBlock { msg: e.to_string() })
348 }
349 Err(Error::ContractExecutionError(error)) => Err(ApiError::ContractError(error)),
350 Err(e) => Err(ApiError::ContractError(ContractExecutionError::from(e.to_string()))),
351 }
352 }
353
354 pub async fn block_number(&self) -> StrictRpcResult {
356 let block = self.api.starknet.lock().await.get_latest_block().map_err(|err| match err {
357 Error::NoBlock => ApiError::BlockNotFound,
358 unknown_error => ApiError::StarknetDevnetError(unknown_error),
359 })?;
360
361 Ok(StarknetResponse::BlockNumber(block.block_number()).into())
362 }
363
364 pub async fn block_hash_and_number(&self) -> StrictRpcResult {
366 let block = self.api.starknet.lock().await.get_latest_block().map_err(|err| match err {
367 Error::NoBlock => ApiError::BlockNotFound,
368 unknown_error => ApiError::StarknetDevnetError(unknown_error),
369 })?;
370
371 Ok(StarknetResponse::BlockHashAndNumber(BlockHashAndNumberOutput {
372 block_hash: block.block_hash(),
373 block_number: block.block_number(),
374 })
375 .into())
376 }
377
378 pub async fn chain_id(&self) -> StrictRpcResult {
380 let chain_id = self.api.starknet.lock().await.chain_id();
381
382 Ok(StarknetResponse::Felt(chain_id.to_felt()).into())
383 }
384
385 pub async fn syncing(&self) -> StrictRpcResult {
387 Ok(StarknetResponse::Syncing(SyncingOutput::False(false)).into())
388 }
389
390 async fn split_block_range(
394 &self,
395 from_block: Option<BlockId>,
396 to_block: Option<BlockId>,
397 ) -> Result<(Option<(u64, u64)>, Option<BlockId>, Option<BlockId>), ApiError> {
398 let origin_caller = match &self.origin_caller {
399 Some(origin_caller) => origin_caller,
400 None => return Ok((None, from_block, to_block)),
401 };
402
403 let fork_block_number = origin_caller.fork_block_number();
404
405 let starknet = self.api.starknet.lock().await;
406
407 let from_block_number = match from_block {
408 Some(BlockId::Tag(BlockTag::Latest | BlockTag::PreConfirmed)) => {
409 return Ok((None, from_block, to_block));
410 }
411 Some(block_id @ (BlockId::Tag(BlockTag::L1Accepted) | BlockId::Hash(_))) => {
412 match starknet.get_block(&block_id) {
413 Ok(block) => block.block_number().0,
414 Err(_) => origin_caller.get_block_number_from_block_id(block_id).await?,
415 }
416 }
417 Some(BlockId::Number(from_block_number)) => from_block_number,
418 None => 0, };
420
421 if from_block_number > fork_block_number {
422 return Ok((None, Some(BlockId::Number(from_block_number)), to_block));
424 }
425
426 let to_block_number = match to_block {
427 Some(BlockId::Tag(BlockTag::Latest | BlockTag::PreConfirmed)) | None => {
430 return Ok((
431 Some((from_block_number, fork_block_number)),
432 Some(BlockId::Number(fork_block_number + 1)),
434 to_block,
435 ));
436 }
437 Some(block_id @ (BlockId::Tag(BlockTag::L1Accepted) | BlockId::Hash(_))) => {
438 match starknet.get_block(&block_id) {
439 Ok(block) => block.block_number().0,
440 Err(_) => origin_caller.get_block_number_from_block_id(block_id).await?,
441 }
442 }
443 Some(BlockId::Number(to_block_number)) => to_block_number,
444 };
445
446 let origin_range = Some((from_block_number, to_block_number));
447 Ok(if to_block_number <= fork_block_number {
448 (origin_range, None, None)
449 } else {
450 (
451 origin_range,
452 Some(BlockId::Number(fork_block_number + 1)),
453 Some(BlockId::Number(to_block_number)),
454 )
455 })
456 }
457
458 pub(crate) async fn fetch_origin_events_chunk(
462 &self,
463 from_origin: u64,
464 to_origin: u64,
465 continuation_token: Option<String>,
466 address: Option<ContractAddress>,
467 keys: Option<Vec<Vec<Felt>>>,
468 chunk_size: u64,
469 ) -> Result<EventsChunk, ApiError> {
470 let origin_caller = self.origin_caller.as_ref().ok_or(ApiError::StarknetDevnetError(
471 Error::UnexpectedInternalError { msg: "Origin caller unexpectedly undefined".into() },
472 ))?;
473
474 let origin_continuation_token = continuation_token
475 .map(|token| token.trim_start_matches(CONTINUATION_TOKEN_ORIGIN_PREFIX).to_string());
476
477 let mut origin_events_chunk: EventsChunk = origin_caller
478 .starknet_client
479 .get_events(
480 starknet_rs_core::types::EventFilter {
481 from_block: Some(ImportedBlockId::Number(from_origin)),
482 to_block: Some(ImportedBlockId::Number(to_origin)),
483 address: address.map(|address| address.into()),
484 keys,
485 },
486 origin_continuation_token,
487 chunk_size,
488 )
489 .await
490 .map_err(|e| {
491 ApiError::StarknetDevnetError(Error::UnexpectedInternalError {
492 msg: format!("Error in fetching origin events: {e:?}"),
493 })
494 })?
495 .into();
496
497 origin_events_chunk.continuation_token = origin_events_chunk
500 .continuation_token
501 .map_or(Some(DEFAULT_CONTINUATION_TOKEN.to_owned()), |token| {
502 Some(CONTINUATION_TOKEN_ORIGIN_PREFIX.to_owned() + &token)
503 });
504
505 Ok(origin_events_chunk)
506 }
507
508 pub async fn get_events(&self, filter: EventFilter) -> StrictRpcResult {
510 let (origin_range, from_local_block_id, to_local_block_id) =
511 self.split_block_range(filter.from_block, filter.to_block).await?;
512
513 let events_chunk = if origin_range.is_some()
515 && filter
516 .continuation_token
517 .clone()
518 .is_none_or(|token| token.starts_with(CONTINUATION_TOKEN_ORIGIN_PREFIX))
519 {
520 #[allow(clippy::unnecessary_unwrap)]
521 #[allow(clippy::expect_used)]
522 let (from_origin, to_origin) =
523 origin_range.expect("Continuation token implies there are more origin events");
524
525 self.fetch_origin_events_chunk(
526 from_origin,
527 to_origin,
528 filter.continuation_token,
529 filter.address,
530 filter.keys,
531 filter.chunk_size,
532 )
533 .await?
534 } else {
535 let pages_read_so_far = filter
536 .continuation_token
537 .unwrap_or(DEFAULT_CONTINUATION_TOKEN.to_string())
538 .parse::<u64>()
539 .map_err(|_| ApiError::InvalidContinuationToken)?;
540
541 let starknet = self.api.starknet.lock().await;
542 let (events, has_more_events) = starknet
543 .get_events(
544 from_local_block_id,
545 to_local_block_id,
546 filter.address,
547 filter.keys,
548 None,
549 pages_read_so_far * filter.chunk_size,
550 Some(filter.chunk_size),
551 )
552 .map_err(|e| match e {
553 Error::NoBlock => ApiError::BlockNotFound,
554 _ => e.into(),
555 })?;
556
557 EventsChunk {
558 events,
559 continuation_token: has_more_events.then(|| (pages_read_so_far + 1).to_string()),
560 }
561 };
562
563 Ok(StarknetResponse::Events(events_chunk).into())
564 }
565
566 pub async fn get_nonce(
568 &self,
569 block_id: BlockId,
570 contract_address: ContractAddress,
571 ) -> StrictRpcResult {
572 let nonce = self
573 .api
574 .starknet
575 .lock()
576 .await
577 .contract_nonce_at_block(&block_id, contract_address)
578 .map_err(|err| match err {
579 Error::NoBlock => ApiError::BlockNotFound,
580 Error::ContractNotFound => ApiError::ContractNotFound,
581 e @ Error::NoStateAtBlock { .. } => ApiError::NoStateAtBlock { msg: e.to_string() },
582 unknown_error => ApiError::StarknetDevnetError(unknown_error),
583 })?;
584
585 Ok(StarknetResponse::Felt(nonce).into())
586 }
587
588 pub async fn simulate_transactions(
590 &self,
591 block_id: BlockId,
592 transactions: Vec<BroadcastedTransaction>,
593 simulation_flags: Vec<SimulationFlag>,
594 ) -> StrictRpcResult {
595 let mut starknet = self.api.starknet.lock().await;
596
597 match starknet.simulate_transactions(&block_id, &transactions, simulation_flags) {
598 Ok(result) => Ok(StarknetResponse::SimulateTransactions(result).into()),
599 Err(Error::ContractNotFound) => Err(ApiError::ContractNotFound),
600 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
601 Err(e @ Error::NoStateAtBlock { .. }) => {
602 Err(ApiError::NoStateAtBlock { msg: e.to_string() })
603 }
604 Err(Error::ContractExecutionErrorInSimulation { failure_index, execution_error }) => {
605 Err(ApiError::TransactionExecutionError { failure_index, execution_error })
606 }
607 Err(e) => Err(ApiError::ContractError(ContractExecutionError::from(e.to_string()))),
608 }
609 }
610
611 pub async fn get_trace_transaction(
613 &self,
614 transaction_hash: TransactionHash,
615 ) -> StrictRpcResult {
616 let starknet = self.api.starknet.lock().await;
617 match starknet.get_transaction_trace_by_hash(transaction_hash) {
618 Ok(result) => Ok(StarknetResponse::TraceTransaction(result).into()),
619 Err(Error::NoTransaction) => Err(ApiError::TransactionNotFound),
620 Err(Error::UnsupportedTransactionType) => Err(ApiError::NoTraceAvailable),
621 Err(err) => Err(err.into()),
622 }
623 }
624
625 pub async fn get_trace_block_transactions(&self, block_id: BlockId) -> StrictRpcResult {
627 let starknet = self.api.starknet.lock().await;
628 match starknet.get_transaction_traces_from_block(&block_id) {
629 Ok(result) => Ok(StarknetResponse::BlockTransactionTraces(result).into()),
630 Err(Error::NoBlock) => Err(ApiError::BlockNotFound),
631 Err(err) => Err(err.into()),
632 }
633 }
634
635 pub async fn get_messages_status(
637 &self,
638 L1TransactionHashInput { transaction_hash }: L1TransactionHashInput,
639 ) -> StrictRpcResult {
640 let starknet = self.api.starknet.lock().await;
641 match starknet.get_messages_status(transaction_hash) {
642 Some(statuses) => Ok(StarknetResponse::MessagesStatusByL1Hash(statuses).into()),
643 None => Err(ApiError::TransactionNotFound),
644 }
645 }
646
647 pub async fn get_predeployed_accounts(
649 &self,
650 params: Option<PredeployedAccountsQuery>,
651 ) -> StrictRpcResult {
652 let mut starknet = self.api.starknet.lock().await;
653 let mut predeployed_accounts: Vec<_> = starknet
654 .get_predeployed_accounts()
655 .into_iter()
656 .map(|acc| SerializableAccount {
657 initial_balance: acc.initial_balance.to_string(),
658 address: acc.account_address,
659 public_key: acc.keys.public_key,
660 private_key: acc.keys.private_key,
661 balance: None,
662 })
663 .collect();
664
665 if let Some(true) =
667 params.unwrap_or(PredeployedAccountsQuery { with_balance: Option::None }).with_balance
668 {
669 for account in predeployed_accounts.iter_mut() {
670 let eth = get_balance_unit(&mut starknet, account.address, FeeUnit::WEI)?;
671 let strk = get_balance_unit(&mut starknet, account.address, FeeUnit::FRI)?;
672
673 account.balance = Some(AccountBalancesResponse { eth, strk });
674 }
675 }
676 Ok(DevnetResponse::PredeployedAccounts(predeployed_accounts).into())
677 }
678
679 pub async fn get_account_balance(&self, params: BalanceQuery) -> StrictRpcResult {
681 let account_address = ContractAddress::new(params.address)
682 .map_err(|e| ApiError::InvalidAddress { msg: e.to_string() })?;
683 let unit = params.unit.unwrap_or(FeeUnit::FRI);
684 let erc20_address = get_erc20_fee_unit_address(&unit);
685
686 let mut starknet = self.api.starknet.lock().await;
687
688 let amount = get_balance(
689 &mut starknet,
690 account_address,
691 erc20_address,
692 params.block_id.unwrap_or(BlockId::Tag(BlockTag::Latest)),
693 )?;
694 Ok(DevnetResponse::AccountBalance(AccountBalanceResponse {
695 amount: amount.to_string(),
696 unit,
697 })
698 .into())
699 }
700
701 pub async fn get_devnet_config(&self) -> StrictRpcResult {
703 Ok(DevnetResponse::DevnetConfig(DevnetConfig {
704 starknet_config: (*self.api.config).clone(),
705 server_config: (*self.api.server_config).clone(),
706 })
707 .into())
708 }
709}