Skip to content

Add sncast get tx-trace - #4546

Open
franciszekjob wants to merge 45 commits into
masterfrom
franciszekjob/3557-get-tx-trace
Open

Add sncast get tx-trace#4546
franciszekjob wants to merge 45 commits into
masterfrom
franciszekjob/3557-get-tx-trace

Conversation

@franciszekjob

@franciszekjob franciszekjob commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Towards #3557

Introduced changes

Add sncast get tx-trace command

Checklist

  • Linked relevant issue
  • Updated relevant documentation
  • Added relevant tests
  • Performed self-review of the code
  • Added changes to CHANGELOG.md

@franciszekjob franciszekjob changed the title WIP Add sncast get tx-trace Aug 18, 2026
@franciszekjob
franciszekjob force-pushed the franciszekjob/3557-get-tx-trace branch 2 times, most recently from f44eb8d to e41c61f Compare August 19, 2026 12:46
Comment on lines 154 to +181
@@ -163,6 +165,20 @@ impl OutputBuilder {
field_value,
)
.unwrap();

for line in value_lines {
if line.is_empty() {
content.push('\n');
} else {
writeln!(
content,
"{}{}",
" ".repeat(field_width + 1),
style(line).yellow()
)
.unwrap();
}
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: This change is needed to align continuation lines with the field value, keeping multiline values (such as revert reason) readable.

Comment thread Cargo.toml Outdated
Comment on lines +79 to +80
starknet-rust = "0.19.0"
# TODO: Restore version once starknet-rust releases new version
starknet-rust = { git = "https://github.com/software-mansion/starknet-rust", rev = "56efca00feffac6beb39f464eca71afa42c1813b" }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: Temporary, due to change in the content type in NoTraceAvailable error.

@franciszekjob
franciszekjob force-pushed the franciszekjob/3557-get-tx-trace branch from 3fdf536 to bf9b114 Compare August 20, 2026 15:24
Comment thread docs/src/starknet/multicall.md
Comment thread crates/sncast/tests/e2e/get/tx_receipt.rs
Comment thread crates/sncast/src/starknet_commands/get/tx_trace.rs
@franciszekjob
franciszekjob marked this pull request as ready for review August 20, 2026 15:58
@franciszekjob
franciszekjob requested a review from a team as a code owner August 20, 2026 15:58
@franciszekjob
franciszekjob requested review from MKowalski8, ddoktorski and integraledelebesgue and removed request for MKowalski8 August 20, 2026 15:58
Comment thread crates/sncast/src/response/get/tx_trace.rs
Comment thread crates/sncast/src/response/get/tx_trace.rs Outdated
Comment thread crates/sncast/src/starknet_commands/get/tx_trace.rs
Comment thread crates/sncast/src/starknet_commands/get/tx_trace.rs
Comment thread crates/sncast/src/response/errors.rs
Comment thread crates/sncast/src/response/get/tx_trace.rs
Comment thread crates/sncast/src/response/get/tx_trace.rs Outdated
Comment thread crates/data-transformer/src/shared/extraction.rs
Comment thread crates/sncast/src/response/get/tx_trace.rs Outdated
@franciszekjob
franciszekjob changed the base branch from master to franciszekjob/global-json-flag August 25, 2026 12:44
@franciszekjob
franciszekjob force-pushed the franciszekjob/3557-get-tx-trace branch 2 times, most recently from bb2b7d8 to a3154a4 Compare August 25, 2026 12:45

fn decode_invocation_json(
invocation: &FunctionInvocation,
json: &mut Value,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why decode_execute_invocation_json takes Option and decode_invocation_json not?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed, 54735d2

json.get_mut("validate_invocation"),
decoder,
);
if let Some(json) = json.get_mut("constructor_invocation") {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we rather throw an error if required field is missing? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, e82b05b

where
S: Serializer,
{
let mut json = serde_json::to_value(&self.trace).map_err(S::Error::custom)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the rationale that it is first parsed to json instead of direct serialization of the struct like for other commands e.g. get tx-receipt?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Direct serialization would return raw felts. We convert to JSON first to preserve the original trace schema while replacing selectors, calldata etc. with decoded values. Mentioned tx-receipt (and others) do not require such transformations.

let builder = append_calls(builder, &invocation.calls, decoder, indent);
let builder = builder
.with_indent(indent)
.felt_field("Class Hash", &invocation.class_hash)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's use padded_felt_field for class hashes and contract addresses

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

#[must_use]
pub fn contract_addresses_by_class_hash(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that this and related code should be placed in starknet_commands/get/tx_trace.rs not in reponse dir

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread crates/sncast/src/starknet_commands/get/tx_trace.rs Outdated
Comment thread crates/sncast/src/response/get/tx_trace.rs Outdated
Comment on lines +124 to +129
fn provider_error_message(error: &ProviderError) -> String {
match error {
ProviderError::StarknetError(error) => error.message().to_string(),
error => error.to_string(),
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to fix it on the starknet-rust side?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it does, changed in software-mansion/starknet-rust#160

builder
}

fn append_full_invocation(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current display seems to be in this order:

  1. root Call Type, Calldata, Caller Address
  2. root Calls
  3. nested call details
  4. root Class Hash, Contract Address, Entry Point Selector etc.

which is very hard to read, please fix it so all root data go first

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


## `--full`
Optional.
Conflicts with: [`--json`](../common.md#--json--j)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If --json is passed directly after sncast there is no conflict and --full is ignored

@franciszekjob franciszekjob Aug 31, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, right. In #4567 I made --json flag global, so the conflict could be defined at clap level. However, clap does not detect it when --json appears before the subcommand. I think the cleanest solution is to validate the --json / --full conflict at runtime remove the now-unnecessary change from mentioned PR.

Runtime validation -> 7758ae0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remind me why actually we can't use both these flags together? 🙏

@franciszekjob
franciszekjob force-pushed the franciszekjob/3557-get-tx-trace branch from 00e1a51 to ff96770 Compare August 27, 2026 11:05
Base automatically changed from franciszekjob/global-json-flag to master August 27, 2026 13:25
@franciszekjob
franciszekjob force-pushed the franciszekjob/3557-get-tx-trace branch 3 times, most recently from c6d690a to 7bbf106 Compare September 1, 2026 09:06
Type: INVOKE
Validate Invocation
Entry Point Selector: __validate__
Contract Address: 0x[..]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not to show the exact values here?

fetch_contract_classes(&provider, class_hashes(&trace)).await;

if !failures.is_empty() {
ui.print_warning(WarningMessage::new(format_class_fetch_warning(&failures)));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be possible to make it part of decoding_warnings so all decoding errors are gathered in one place? It should work better for json output then


fn selector(&self, invocation: &FunctionInvocation) -> String {
if let Some(abi) = self.sierra_abis.get(&invocation.class_hash.into_())
&& let Some(function) =

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invocation.class_hash.into_() is evaluated a few times in this function, let's just add

let class_hash: ClassHash = invocation.class_hash.into_();

also in the functions below

format!("Some trace data is shown as raw felts:\n{details}")
}

#[must_use]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#[must_use]

Comment on lines +171 to +174
let builder = if decoder.decoding_warnings().is_empty() {
OutputBuilder::new()
} else {
let warning_message = format_decoding_warning(&decoder.decoding_warnings());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: decoder.decoding_warnings() is called twice

sierra_abis: HashMap<ClassHash, Vec<AbiEntry>>,
legacy_class_hashes: HashSet<ClassHash>,
legacy_selectors: HashMap<(ClassHash, Felt), String>,
decoding_warnings: RefCell<BTreeSet<TraceDecodingWarning>>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we omit using interior mutability here?


## `--full`
Optional.
Conflicts with: [`--json`](../common.md#--json--j)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you remind me why actually we can't use both these flags together? 🙏

let results = stream::iter(class_hashes)
.map(|class_hash| async move {
match provider
.get_class(BlockId::Tag(BlockTag::PreConfirmed), *class_hash)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why pre confirmed and not latest?

.field("Calldata", &decoder.calldata(invocation))
.padded_felt_field("Caller Address", &invocation.caller_address)
.padded_felt_field("Class Hash", &invocation.class_hash)
.padded_felt_field("Contract Address", &invocation.contract_address)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
.padded_felt_field("Contract Address", &invocation.contract_address)
.contract_address(&invocation.contract_address)


## Overview

Starknet Foundry `sncast` supports the inspection of transaction statuses on a given network with the `sncast get tx-status` command.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can be updated

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants