Skip to content

Commit 8c8985b

Browse files
committed
feat: add devnet_getStatus RPC method (#942)
Add a new devnet_getStatus JSON-RPC method that returns: - block_count, transaction_count, pre_confirmed_tx_count - chain_id, protocol_version - is_forked flag and fork_config (url, block) when forking - impersonated_accounts list and auto_impersonate flag Includes new helper methods in starknet-devnet-core and integration tests.
1 parent e4bd050 commit 8c8985b

11 files changed

Lines changed: 179 additions & 8 deletions

File tree

crates/starknet-devnet-core/src/starknet/cheats.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,10 @@ impl Cheats {
2121
pub(crate) fn set_auto_impersonate(&mut self, auto_impersonation: bool) {
2222
self.auto_impersonate = auto_impersonation;
2323
}
24+
pub(crate) fn get_impersonated_accounts(&self) -> &HashSet<ContractAddress> {
25+
&self.impersonated_accounts
26+
}
27+
pub(crate) fn is_auto_impersonate(&self) -> bool {
28+
self.auto_impersonate
29+
}
2430
}

crates/starknet-devnet-core/src/starknet/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,36 @@ impl Starknet {
925925
self.config.chain_id
926926
}
927927

928+
/// Returns true if devnet is running in forking mode
929+
pub fn is_forked(&self) -> bool {
930+
self.config.fork_config.url.is_some()
931+
}
932+
933+
/// Returns the total number of accepted blocks
934+
pub fn get_block_count(&self) -> usize {
935+
self.blocks.num_to_hash.len()
936+
}
937+
938+
/// Returns the total number of transactions across all blocks (including pre_confirmed)
939+
pub fn get_transaction_count(&self) -> usize {
940+
self.transactions.len()
941+
}
942+
943+
/// Returns the number of transactions in the pre-confirmed block
944+
pub fn get_pre_confirmed_tx_count(&self) -> usize {
945+
self.blocks.pre_confirmed_block.get_transactions().len()
946+
}
947+
948+
/// Returns addresses of currently impersonated accounts
949+
pub fn get_impersonated_accounts(&self) -> Vec<ContractAddress> {
950+
self.cheats.get_impersonated_accounts().iter().copied().collect()
951+
}
952+
953+
/// Returns whether auto-impersonate is enabled
954+
pub fn is_auto_impersonate(&self) -> bool {
955+
self.cheats.is_auto_impersonate()
956+
}
957+
928958
pub fn add_deploy_account_transaction(
929959
&mut self,
930960
deploy_account_transaction: BroadcastedDeployAccountTransaction,

crates/starknet-devnet-core/src/transactions.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ impl StarknetTransactions {
3636
pub fn remove(&mut self, transaction_hash: &TransactionHash) -> Option<StarknetTransaction> {
3737
self.0.shift_remove(transaction_hash)
3838
}
39+
40+
pub fn len(&self) -> usize {
41+
self.0.len()
42+
}
3943
}
4044

4145
impl HashIdentifiedMut for StarknetTransactions {

crates/starknet-devnet-server/src/api/endpoints.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ use crate::api::account_helpers::{
2424
get_erc20_fee_unit_address,
2525
};
2626
use crate::api::models::{
27-
AccountBalanceResponse, AccountBalancesResponse, DevnetResponse, ProveTransactionResponse,
28-
SerializableAccount, StarknetExtResponse, StarknetResponse, StorageResult,
27+
AccountBalanceResponse, AccountBalancesResponse, DevnetResponse, DevnetStatus, ForkStatus,
28+
ProveTransactionResponse, SerializableAccount, StarknetExtResponse, StarknetResponse,
29+
StorageResult,
2930
};
3031
use crate::api::{JsonRpcHandler, RPC_SPEC_VERSION};
3132
use crate::config::DevnetConfig;
@@ -856,4 +857,32 @@ impl JsonRpcHandler {
856857
})
857858
.into())
858859
}
860+
861+
/// devnet_getStatus
862+
pub async fn get_devnet_status(&self) -> StrictRpcResult {
863+
let starknet = self.api.starknet.lock().await;
864+
865+
let is_forked = starknet.is_forked();
866+
let fork_config = if is_forked {
867+
Some(ForkStatus {
868+
url: starknet.config.fork_config.url.as_ref().map(|u| u.to_string()),
869+
block: starknet.config.fork_config.block_number,
870+
})
871+
} else {
872+
None
873+
};
874+
875+
Ok(DevnetResponse::DevnetStatus(DevnetStatus {
876+
block_count: starknet.get_block_count(),
877+
transaction_count: starknet.get_transaction_count(),
878+
pre_confirmed_tx_count: starknet.get_pre_confirmed_tx_count(),
879+
chain_id: starknet.chain_id().to_string(),
880+
protocol_version: RPC_SPEC_VERSION.to_string(),
881+
is_forked,
882+
fork_config,
883+
impersonated_accounts: starknet.get_impersonated_accounts(),
884+
auto_impersonate: starknet.is_auto_impersonate(),
885+
})
886+
.into())
887+
}
859888
}

crates/starknet-devnet-server/src/api/json_rpc_handler.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,7 @@ impl JsonRpcHandler {
549549
DevnetSpecRequest::AccountBalance(data) => self.get_account_balance(data).await,
550550
DevnetSpecRequest::Mint(data) => self.mint(data).await,
551551
DevnetSpecRequest::DevnetConfig => self.get_devnet_config().await,
552+
DevnetSpecRequest::DevnetStatus => self.get_devnet_status().await,
552553
}
553554
}
554555

crates/starknet-devnet-server/src/api/models/json_rpc_request.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ pub enum DevnetSpecRequest {
181181
Mint(MintTokensRequest),
182182
#[serde(rename = "devnet_getConfig", with = "empty_params")]
183183
DevnetConfig,
184+
#[serde(rename = "devnet_getStatus", with = "empty_params")]
185+
DevnetStatus,
184186
}
185187

186188
#[cfg_attr(test, derive(Debug))]
@@ -385,7 +387,8 @@ impl DevnetSpecRequest {
385387
| Self::Restart(_)
386388
| Self::PredeployedAccounts(_)
387389
| Self::AccountBalance(_)
388-
| Self::DevnetConfig => false,
390+
| Self::DevnetConfig
391+
| Self::DevnetStatus => false,
389392
}
390393
}
391394

@@ -413,7 +416,8 @@ impl DevnetSpecRequest {
413416
| Self::Restart(_)
414417
| Self::PredeployedAccounts(_)
415418
| Self::AccountBalance(_)
416-
| Self::DevnetConfig => false,
419+
| Self::DevnetConfig
420+
| Self::DevnetStatus => false,
417421
}
418422
}
419423
}

crates/starknet-devnet-server/src/api/models/json_rpc_response.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ use starknet_types::starknet_api::block::BlockNumber;
1717

1818
use crate::api::models::{
1919
AbortedBlocks, AcceptedOnL1Blocks, AccountBalanceResponse, BlockHashAndNumberOutput,
20-
CreatedBlock, DeclareTransactionOutput, DeployAccountTransactionOutput, DumpResponseBody,
21-
FlushedMessages, IncreaseTimeResponse, MessageHash, MessagingLoadAddress, MintTokensResponse,
22-
ProveTransactionResponse, SerializableAccount, SetTimeResponse, StorageResult, SyncingOutput,
23-
TransactionHashOutput,
20+
CreatedBlock, DeclareTransactionOutput, DeployAccountTransactionOutput, DevnetStatus,
21+
DumpResponseBody, FlushedMessages, IncreaseTimeResponse, MessageHash, MessagingLoadAddress,
22+
MintTokensResponse, ProveTransactionResponse, SerializableAccount, SetTimeResponse,
23+
StorageResult, SyncingOutput, TransactionHashOutput,
2424
};
2525
use crate::config::DevnetConfig;
2626

@@ -115,6 +115,7 @@ pub enum DevnetResponse {
115115
MintTokens(MintTokensResponse),
116116
DevnetConfig(DevnetConfig),
117117
DevnetDump(DumpResponseBody),
118+
DevnetStatus(DevnetStatus),
118119
}
119120

120121
#[cfg(test)]

crates/starknet-devnet-server/src/api/models/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,20 @@ pub struct ForkStatus {
509509
pub block: Option<u64>,
510510
}
511511

512+
#[derive(Serialize)]
513+
pub struct DevnetStatus {
514+
pub block_count: usize,
515+
pub transaction_count: usize,
516+
pub pre_confirmed_tx_count: usize,
517+
pub chain_id: String,
518+
pub protocol_version: String,
519+
pub is_forked: bool,
520+
#[serde(skip_serializing_if = "Option::is_none")]
521+
pub fork_config: Option<ForkStatus>,
522+
pub impersonated_accounts: Vec<ContractAddress>,
523+
pub auto_impersonate: bool,
524+
}
525+
512526
#[derive(Serialize, Deserialize)]
513527
pub struct FlushedMessages {
514528
pub messages_to_l1: Vec<MessageToL1>,
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use serde_json::json;
2+
3+
use crate::common::background_devnet::BackgroundDevnet;
4+
5+
#[tokio::test]
6+
async fn devnet_get_status_initial_state() {
7+
let devnet = BackgroundDevnet::spawn().await.expect("Could not start Devnet");
8+
9+
let status = devnet.send_custom_rpc("devnet_getStatus", json!({})).await.unwrap();
10+
11+
// Verify structure and initial values
12+
assert_eq!(status["block_count"].as_u64().unwrap(), 1, "Genesis block should be present");
13+
assert_eq!(status["transaction_count"].as_u64().unwrap(), 0, "No transactions initially");
14+
assert_eq!(status["pre_confirmed_tx_count"].as_u64().unwrap(), 0);
15+
assert!(status["chain_id"].as_str().unwrap().contains("SEPOLIA"));
16+
assert_eq!(status["protocol_version"].as_str().unwrap(), "0.10.2");
17+
assert!(!status["is_forked"].as_bool().unwrap());
18+
// fork_config is only present when forking
19+
assert!(
20+
status.get("fork_config").is_none_or(|v| v.is_null()),
21+
"fork_config should be absent/omitted when not forking"
22+
);
23+
assert!(status["impersonated_accounts"].as_array().unwrap().is_empty());
24+
assert!(!status["auto_impersonate"].as_bool().unwrap());
25+
}
26+
27+
#[tokio::test]
28+
async fn devnet_get_status_after_interaction() {
29+
let devnet = BackgroundDevnet::spawn().await.expect("Could not start Devnet");
30+
31+
// Create a block to change block count
32+
devnet.create_block().await.unwrap();
33+
34+
let status = devnet.send_custom_rpc("devnet_getStatus", json!({})).await.unwrap();
35+
assert!(
36+
status["block_count"].as_u64().unwrap() >= 2,
37+
"Block count should increase after creating a block"
38+
);
39+
40+
// Mint tokens to generate a transaction
41+
let (_, account_address) = devnet.get_first_predeployed_account().await;
42+
let mint_amount = 1_000_000u128;
43+
devnet.mint(account_address, mint_amount).await;
44+
45+
let status = devnet.send_custom_rpc("devnet_getStatus", json!({})).await.unwrap();
46+
assert!(
47+
status["transaction_count"].as_u64().unwrap() >= 1,
48+
"Transaction count should increase after minting"
49+
);
50+
}

tests/integration/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ mod get_block_txs_count;
2323
mod get_block_with_txs_and_receipts;
2424
mod get_class;
2525
mod get_class_hash_at;
26+
mod get_devnet_status;
2627
mod get_events;
2728
mod get_transaction_by_block_id_and_index;
2829
mod get_transaction_by_hash;

0 commit comments

Comments
 (0)