Skip to content

Commit 807c298

Browse files
authored
Add sncast get nonce (#4210)
<!-- Reference any GitHub issues resolved by this PR --> Towards #4144 ## Stack - #4215 - #4210 ## Introduced changes <!-- A brief description of the changes --> - `sncast nonce` command with flags: - `--contract-address` - `--block-id` ## Checklist <!-- Make sure all of these are complete --> - [x] Linked relevant issue - [x] Updated relevant documentation - [x] Added relevant tests - [ ] Performed self-review of the code - [x] Added changes to `CHANGELOG.md`
1 parent 53a59b9 commit 807c298

10 files changed

Lines changed: 218 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Cast
11+
12+
#### Added
13+
14+
- `sncast get nonce` command to get the nonce of a contract.
15+
1016
### Forge
1117

1218
#### Added

crates/sncast/src/response/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod errors;
99
pub mod explorer_link;
1010
pub mod invoke;
1111
pub mod multicall;
12+
pub mod nonce;
1213
pub mod script;
1314
pub mod show_config;
1415
pub mod transformed_call;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use crate::response::cast_message::SncastCommandMessage;
2+
use conversions::serde::serialize::CairoSerialize;
3+
use conversions::string::IntoHexStr;
4+
use foundry_ui::styling;
5+
use serde::Serialize;
6+
use starknet_types_core::felt::Felt;
7+
8+
#[derive(Serialize, CairoSerialize, Clone)]
9+
pub struct NonceResponse {
10+
pub nonce: Felt,
11+
}
12+
13+
impl SncastCommandMessage for NonceResponse {
14+
fn text(&self) -> String {
15+
styling::OutputBuilder::new()
16+
.success_message("Nonce retrieved")
17+
.blank_line()
18+
.field("Nonce", &self.nonce.into_hex_string())
19+
.build()
20+
}
21+
}

crates/sncast/src/starknet_commands/get/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use sncast::helpers::configuration::CastConfig;
33
use sncast::response::ui::UI;
44

55
pub mod balance;
6+
pub mod nonce;
67
pub mod tx_status;
78

89
#[derive(Args)]
@@ -19,13 +20,18 @@ pub enum GetCommands {
1920

2021
/// Fetch balance of the account for specified token
2122
Balance(balance::Balance),
23+
24+
/// Get nonce of a contract
25+
Nonce(nonce::Nonce),
2226
}
2327

2428
pub async fn get(get: Get, config: CastConfig, ui: &UI) -> anyhow::Result<()> {
2529
match get.command {
2630
GetCommands::TxStatus(status) => tx_status::tx_status(status, config, ui).await?,
2731

2832
GetCommands::Balance(balance) => balance::balance(balance, config, ui).await?,
33+
34+
GetCommands::Nonce(nonce) => nonce::nonce(nonce, config, ui).await?,
2935
}
3036

3137
Ok(())
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use anyhow::Result;
2+
use clap::Args;
3+
use sncast::get_block_id;
4+
use sncast::helpers::command::process_command_result;
5+
use sncast::helpers::configuration::CastConfig;
6+
use sncast::helpers::rpc::RpcArgs;
7+
use sncast::response::errors::{SNCastProviderError, StarknetCommandError};
8+
use sncast::response::nonce::NonceResponse;
9+
use sncast::response::ui::UI;
10+
use starknet_rust::providers::jsonrpc::HttpTransport;
11+
use starknet_rust::providers::{JsonRpcClient, Provider};
12+
use starknet_types_core::felt::Felt;
13+
14+
#[derive(Debug, Args)]
15+
#[command(about = "Get the nonce of a contract")]
16+
pub struct Nonce {
17+
/// Address of the contract
18+
pub contract_address: Felt,
19+
20+
/// Block identifier on which nonce should be fetched.
21+
/// Possible values: `pre_confirmed`, `latest`, block hash (0x prefixed string)
22+
/// and block number (u64)
23+
#[arg(short, long, default_value = "pre_confirmed")]
24+
pub block_id: String,
25+
26+
#[command(flatten)]
27+
pub rpc: RpcArgs,
28+
}
29+
30+
pub async fn nonce(nonce: Nonce, config: CastConfig, ui: &UI) -> Result<()> {
31+
let provider = nonce.rpc.get_provider(&config, ui).await?;
32+
33+
let result = get_nonce(&provider, nonce.contract_address, &nonce.block_id).await;
34+
35+
process_command_result("get nonce", result, ui, None);
36+
Ok(())
37+
}
38+
39+
pub async fn get_nonce(
40+
provider: &JsonRpcClient<HttpTransport>,
41+
contract_address: Felt,
42+
block_id: &str,
43+
) -> Result<NonceResponse> {
44+
let block_id = get_block_id(block_id)?;
45+
let nonce = provider
46+
.get_nonce(block_id, contract_address)
47+
.await
48+
.map_err(|err| StarknetCommandError::ProviderError(SNCastProviderError::from(err)))?;
49+
Ok(NonceResponse { nonce })
50+
}

crates/sncast/tests/e2e/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod fee;
1111
mod invoke;
1212
mod main_tests;
1313
mod multicall;
14+
mod nonce;
1415
mod script;
1516
mod serialize;
1617
mod show_config;

crates/sncast/tests/e2e/nonce.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use crate::helpers::constants::{DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS, URL};
2+
use crate::helpers::runner::runner;
3+
use indoc::indoc;
4+
use shared::test_utils::output_assert::assert_stderr_contains;
5+
6+
#[tokio::test]
7+
async fn test_happy_case() {
8+
let args = vec![
9+
"get",
10+
"nonce",
11+
DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS,
12+
"--url",
13+
URL,
14+
];
15+
let snapbox = runner(&args);
16+
17+
snapbox.assert().success().stdout_eq(indoc! {r"
18+
Success: Nonce retrieved
19+
20+
Nonce: 0x[..]
21+
"});
22+
}
23+
24+
#[tokio::test]
25+
async fn test_happy_case_with_block_id() {
26+
let args = vec![
27+
"get",
28+
"nonce",
29+
DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS,
30+
"--block-id",
31+
"latest",
32+
"--url",
33+
URL,
34+
];
35+
let snapbox = runner(&args);
36+
37+
snapbox.assert().success().stdout_eq(indoc! {r"
38+
Success: Nonce retrieved
39+
40+
Nonce: 0x[..]
41+
"});
42+
}
43+
44+
#[tokio::test]
45+
async fn test_happy_case_json() {
46+
let args = vec![
47+
"--json",
48+
"get",
49+
"nonce",
50+
DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS,
51+
"--url",
52+
URL,
53+
];
54+
let snapbox = runner(&args);
55+
56+
snapbox.assert().success().stdout_eq(indoc! {r#"
57+
{"command":"get nonce","nonce":"0x[..]","type":"response"}
58+
"#});
59+
}
60+
61+
#[tokio::test]
62+
async fn test_nonexistent_contract_address() {
63+
let args = vec!["get", "nonce", "0x0", "--url", URL];
64+
let snapbox = runner(&args);
65+
let output = snapbox.assert().success();
66+
67+
assert_stderr_contains(
68+
output,
69+
indoc! {r"
70+
Command: get nonce
71+
Error: There is no contract at the specified address
72+
"},
73+
);
74+
}
75+
76+
#[tokio::test]
77+
async fn test_invalid_block_id() {
78+
let args = vec![
79+
"get",
80+
"nonce",
81+
DEVNET_PREDEPLOYED_ACCOUNT_ADDRESS,
82+
"--block-id",
83+
"invalid_block",
84+
"--url",
85+
URL,
86+
];
87+
let snapbox = runner(&args);
88+
let output = snapbox.assert().success();
89+
90+
assert_stderr_contains(
91+
output,
92+
indoc! {r"
93+
Command: get nonce
94+
Error: Incorrect value passed for block_id = invalid_block. Possible values are `pre_confirmed`, `latest`, block hash (hex) and block number (u64)
95+
"},
96+
);
97+
}

docs/src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@
156156
* [completions](appendix/sncast/completions.md)
157157
* [get](appendix/sncast/get/get.md)
158158
* [balance](appendix/sncast/get/balance.md)
159+
* [nonce](appendix/sncast/get/nonce.md)
159160
* [tx-status](appendix/sncast/get/tx-status.md)
160161
* [utils](appendix/sncast/utils/utils.md)
161162
* [serialize](appendix/sncast/utils/serialize.md)

docs/src/appendix/sncast.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@
1919
* [run](./sncast/script/run.md)
2020
* [show-config](./sncast/show_config.md)
2121
* [get](./sncast/get/get.md)
22-
* [balance](./sncast/get/balance.md)
23-
* [tx-status](./sncast/get/tx-status.md)
22+
* [balance](./sncast/get/balance.md)
23+
* [nonce](./sncast/get/nonce.md)
24+
* [tx-status](./sncast/get/tx-status.md)
2425
* [utils](./sncast/utils/utils.md)
2526
* [serialize](./sncast/utils/serialize.md)
2627
* [class-hash](./sncast/utils/class_hash.md)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# `get nonce`
2+
3+
Get the nonce of the specified contract.
4+
5+
## `<CONTRACT_ADDRESS>`
6+
7+
Required.
8+
9+
Address of the contract in hex (prefixed with '0x') or decimal representation.
10+
11+
## `--block-id, -b <BLOCK_ID>`
12+
Optional.
13+
14+
Block identifier on which nonce should be fetched.
15+
Possible values: `pre_confirmed`, `latest`, block hash (0x prefixed string), and block number (u64).
16+
`pre_confirmed` is used as a default value.
17+
18+
## `--url, -u <RPC_URL>`
19+
Optional.
20+
21+
Starknet RPC node url address.
22+
23+
Overrides url from `snfoundry.toml`.
24+
25+
## `--network <NETWORK>`
26+
Optional.
27+
28+
Use predefined network with public provider
29+
30+
Possible values: `mainnet`, `sepolia`, `devnet`.
31+
32+
Overrides network from `snfoundry.toml`.

0 commit comments

Comments
 (0)