Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@ impl Sdk {
trace!("document_replace: broadcasting and awaiting response");
let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();
trace!("document_replace: broadcast completed");

match proof_result {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedDocuments(documents) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/rs-sdk/src/platform/tokens/transitions/burn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenBalance(owner_id, remaining_balance) => {
Expand Down
3 changes: 2 additions & 1 deletion packages/rs-sdk/src/platform/tokens/transitions/claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenActionWithDocument(document) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenActionWithDocument(document) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenActionWithDocument(doc) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenBalance(owner_id, balance) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenActionWithDocument(document) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-sdk/src/platform/tokens/transitions/freeze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;

.await?
.into_inner();
match proof_result {
StateTransitionProofResult::VerifiedTokenIdentityInfo(owner_id_result, info) => {
Ok(FreezeResult::IdentityInfo(owner_id_result, info))
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-sdk/src/platform/tokens/transitions/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;

.await?
.into_inner();
match proof_result {
StateTransitionProofResult::VerifiedTokenBalance(recipient_id_result, new_balance) => {
Ok(MintResult::TokenBalance(recipient_id_result, new_balance))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;

.await?
.into_inner();
match proof_result {
StateTransitionProofResult::VerifiedTokenPricingSchedule(owner_id, schedule) => {
Ok(SetPriceResult::PricingSchedule(owner_id, schedule))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;
.await?
.into_inner();

match proof_result {
StateTransitionProofResult::VerifiedTokenIdentitiesBalances(balances) => {
Expand Down
4 changes: 2 additions & 2 deletions packages/rs-sdk/src/platform/tokens/transitions/unfreeze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,8 @@ impl Sdk {

let proof_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, put_settings)
.await?;

.await?
.into_inner();
match proof_result {
StateTransitionProofResult::VerifiedTokenIdentityInfo(owner_id_result, info) => {
Ok(UnfreezeResult::IdentityInfo(owner_id_result, info))
Expand Down
2 changes: 2 additions & 0 deletions packages/rs-sdk/src/platform/transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod shield_from_asset_lock;
pub mod shielded_transfer;
#[cfg(feature = "shielded")]
pub mod shielded_withdrawal;
pub mod state_transition_result;
pub mod top_up_address;
pub mod top_up_identity;
pub mod top_up_identity_from_addresses;
Expand All @@ -32,4 +33,5 @@ pub(crate) mod validation;
pub mod vote;
pub mod waitable;
pub mod withdraw_from_identity;
pub use state_transition_result::StateTransitionResult;
pub use txid::TxId;
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ impl<S: Signer<PlatformAddress>> WithdrawAddressFunds<S> for Sdk {
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedAddressInfos(address_infos_map) => {
let mut expected_addresses: BTreeSet<PlatformAddress> =
Expand Down
13 changes: 10 additions & 3 deletions packages/rs-sdk/src/platform/transition/broadcast.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::broadcast_request::BroadcastRequestForStateTransition;
use super::put_settings::PutSettings;
use super::state_transition_result::StateTransitionResult;
use crate::error::StateTransitionBroadcastError;
use crate::platform::block_info_from_metadata::block_info_from_metadata;
use crate::sync::retry;
Expand Down Expand Up @@ -30,7 +31,7 @@ pub trait BroadcastStateTransition {
&self,
sdk: &Sdk,
settings: Option<PutSettings>,
) -> Result<T, Error>;
) -> Result<StateTransitionResult<T>, Error>;
}

#[async_trait::async_trait]
Expand Down Expand Up @@ -264,8 +265,14 @@ impl BroadcastStateTransition for StateTransition {
&self,
sdk: &Sdk,
settings: Option<PutSettings>,
) -> Result<T, Error> {
) -> Result<StateTransitionResult<T>, Error> {
trace!(state_transition = %self.name(), "broadcast_and_wait: start");

// Compute the transition hash deterministically BEFORE broadcast.
// This is a SHA-256 hash of the serialized StateTransition and does
// not depend on blockchain state, so there is no race condition.
let transition_hash = self.transaction_id()?;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

💬 Nitpick: transaction_id() serializes the state transition multiple times in the broadcast_and_wait flow

In broadcast_and_wait, self.transaction_id()? is called at line 274. Then self.broadcast() calls self.transaction_id() again inside trace!() at line 42-44, and self.wait_for_response() does the same at line 101-102. When trace-level logging is enabled, this results in 3 serializations of the state transition. This is negligible in practice since trace logging is rarely active in production, but the hash could be passed into the inner methods to avoid the redundancy.

source: ['claude']


trace!("broadcast_and_wait: step 1 - broadcasting");
self.broadcast(sdk, settings).await?;
trace!("broadcast_and_wait: step 2 - waiting for response");
Expand All @@ -274,6 +281,6 @@ impl BroadcastStateTransition for StateTransition {
Ok(_) => trace!("broadcast_and_wait: complete success"),
Err(e) => warn!(error = ?e, "broadcast_and_wait: failed"),
}
result
result.map(|inner| StateTransitionResult::new(inner, transition_hash))
}
}
1 change: 1 addition & 0 deletions packages/rs-sdk/src/platform/transition/put_identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ async fn put_identity_with_address_funding<
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedIdentityFullWithAddressInfos(
proved_identity,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use std::ops::Deref;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good question. Yes, it would be cleaner to include the transition hash directly in the result types (DocumentCreateResult, MintResult, etc.) rather than using a generic wrapper.

The wrapper approach (StateTransitionResult<T>) was chosen because:

  1. It avoids modifying 20+ result enums individually
  2. The hash is a property of the broadcast operation, not the result type itself

But Pasta's right that embedding it in T would be simpler for consumers — no .into_inner() calls, no wrapper to deal with. The downside is every result type needs a transition_hash field added.

I'd lean toward keeping the wrapper for now since it's non-invasive, but happy to refactor if you prefer the embedded approach. What's your preference?


/// Wrapper that bundles a state transition proof result with the transition hash.
///
/// The transition hash (also known as the transaction ID) is computed deterministically
/// from the serialized `StateTransition` before broadcast, so it does not depend on
/// blockchain state and there is no race condition.
///
/// `StateTransitionResult<T>` implements `Deref<Target = T>`, so existing code that
/// only needs the inner result can use it transparently.
#[derive(Debug, Clone)]
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "state_transition_result.rs" | head -5

Repository: dashpay/platform

Length of output: 128


🏁 Script executed:

cat -n packages/rs-sdk/src/platform/transition/state_transition_result.rs | head -50

Repository: dashpay/platform

Length of output: 2044


🏁 Script executed:

git log --oneline --all | grep -i "22fc89c1c\|state.transition.result" | head -10

Repository: dashpay/platform

Length of output: 144


🏁 Script executed:

git show --stat 22fc89c1c 2>/dev/null | head -30

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git log --all --oneline --decorate | grep -i "state.transition" | head -20

Repository: dashpay/platform

Length of output: 1741


🏁 Script executed:

git log --all --grep="PartialEq\|Eq" --oneline | head -10

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

rg "StateTransitionResult" packages/rs-sdk/src/platform/transition/ -A 5 -B 2

Repository: dashpay/platform

Length of output: 10366


🏁 Script executed:

git log --all --oneline packages/rs-sdk/src/platform/transition/state_transition_result.rs | head -10

Repository: dashpay/platform

Length of output: 130


🏁 Script executed:

git show d6f4eb9ac --stat | head -40

Repository: dashpay/platform

Length of output: 2283


🏁 Script executed:

git log --all --oneline --grep="22fc89c1c" | head -5

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git log --all --oneline | grep -E "22fc89c1c|3077" | head -5

Repository: dashpay/platform

Length of output: 135


🏁 Script executed:

git show d6f4eb9ac:packages/rs-sdk/src/platform/transition/state_transition_result.rs 2>/dev/null | head -20

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git show d6f4eb9ac --format=fuller | head -100

Repository: dashpay/platform

Length of output: 4611


🏁 Script executed:

git log --all --oneline -S "PartialEq" -- packages/rs-sdk/src/platform/transition/state_transition_result.rs | head -10

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git log --all --oneline -S "Eq" -- packages/rs-sdk/src/platform/transition/state_transition_result.rs | head -10

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git show d6f4eb9ac -- packages/rs-sdk/src/platform/transition/state_transition_result.rs | head -50

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git show d6f4eb9ac:packages/rs-sdk/src/platform/transition/state_transition_result.rs 2>&1 | cat

Repository: dashpay/platform

Length of output: 179


🏁 Script executed:

git log --all --pretty=format:"%H %s" | grep "3077"

Repository: dashpay/platform

Length of output: 408


🏁 Script executed:

git show --name-status d6f4eb9ac | grep "state_transition_result"

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git diff d6f4eb9ac~1 d6f4eb9ac -- packages/rs-sdk/src/platform/transition/state_transition_result.rs

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git log --all --oneline -S "StateTransitionResult" -- "**.rs" | head -20

Repository: dashpay/platform

Length of output: 146


🏁 Script executed:

git log --oneline --all packages/rs-sdk/src/platform/transition/ | head -20

Repository: dashpay/platform

Length of output: 1669


🏁 Script executed:

git show HEAD:packages/rs-sdk/src/platform/transition/state_transition_result.rs 2>&1 | head -30

Repository: dashpay/platform

Length of output: 1067


🏁 Script executed:

rg "PartialEq.*Eq" packages/rs-sdk/src/platform/transition/ -B 2

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git show 07b2e5cba --format=fuller | head -50

Repository: dashpay/platform

Length of output: 2109


🏁 Script executed:

git show 07b2e5cba -- packages/rs-sdk/src/platform/transition/state_transition_result.rs | head -80

Repository: dashpay/platform

Length of output: 2982


🏁 Script executed:

git log --all -p -- packages/rs-sdk/src/platform/transition/state_transition_result.rs | grep -A 3 -B 3 "PartialEq\|Eq" | head -40

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

git show 07b2e5cba --stat | grep "state_transition_result"

Repository: dashpay/platform

Length of output: 245


🏁 Script executed:

git log --all --oneline --grep="post-review\|22fc89c1c" | head -10

Repository: dashpay/platform

Length of output: 42


🏁 Script executed:

rg "StateTransitionResult" packages/rs-sdk/src/platform/transition/broadcast.rs -B 2 -A 2 | head -50

Repository: dashpay/platform

Length of output: 1363


Add PartialEq and Eq derives for test assertions.

The struct only derives Debug and Clone, but PartialEq and Eq would enable assert_eq! comparisons in tests. Both should be unconditionally derived.

🛠️ Proposed fix
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct StateTransitionResult<T> {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/rs-sdk/src/platform/transition/state_transition_result.rs` at line
11, Add PartialEq and Eq to the derive list for the StateTransitionResult struct
so tests can use assert_eq!; update the existing #[derive(Debug, Clone)] line to
#[derive(Debug, Clone, PartialEq, Eq)] (i.e., extend the derive on the struct
defined in state_transition_result.rs) and run cargo test to verify
equality-based assertions now work.

pub struct StateTransitionResult<T> {
Comment on lines +3 to +12
Copy link
Member

Choose a reason for hiding this comment

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

claw; would it make more sense for the T to just include the transition hash? is that possible?

inner: T,
transition_hash: [u8; 32],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🟡 Suggestion: Use the existing TxId newtype instead of raw [u8; 32] for transition_hash

The transition_hash field uses [u8; 32], but a TxId newtype wrapping [u8; 32] already exists in transition/txid.rs and is re-exported from the same module (pub use txid::TxId). Using TxId would provide domain-level type safety, preventing accidental use of an arbitrary 32-byte array as a transition hash, and would give callers access to future TxId methods (like is_confirmed) without a conversion step.

source: ['claude']

🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-sdk/src/platform/transition/state_transition_result.rs`:
- [SUGGESTION] line 14: Use the existing TxId newtype instead of raw [u8; 32] for transition_hash
  The `transition_hash` field uses `[u8; 32]`, but a `TxId` newtype wrapping `[u8; 32]` already exists in `transition/txid.rs` and is re-exported from the same module (`pub use txid::TxId`). Using `TxId` would provide domain-level type safety, preventing accidental use of an arbitrary 32-byte array as a transition hash, and would give callers access to future `TxId` methods (like `is_confirmed`) without a conversion step.

}

impl<T> StateTransitionResult<T> {
/// Creates a new result bundling the proof result with the transition hash.
pub fn new(inner: T, transition_hash: [u8; 32]) -> Self {
Self {
inner,
transition_hash,
}
}

/// Returns the transition hash (transaction ID) as a 32-byte array.
pub fn transition_hash(&self) -> [u8; 32] {
self.transition_hash
}

/// Consumes this wrapper, returning the inner result and the transition hash.
pub fn into_parts(self) -> (T, [u8; 32]) {
(self.inner, self.transition_hash)
}

/// Consumes this wrapper, returning just the inner result.
pub fn into_inner(self) -> T {
self.inner
}

/// Maps the inner value, preserving the transition hash.
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> StateTransitionResult<U> {
StateTransitionResult {
inner: f(self.inner),
transition_hash: self.transition_hash,
}
}
}

impl<T> Deref for StateTransitionResult<T> {
type Target = T;

fn deref(&self) -> &T {
&self.inner
}
}
Comment on lines +1 to +56
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🟡 Suggestion: No unit tests for StateTransitionResult

The new type has 6 public methods (new, transition_hash, into_parts, into_inner, map) plus a Deref impl, but no #[cfg(test)] module. While the type is simple, unit tests would lock the contract — especially that into_parts() returns both components, that map preserves the hash, and that Deref provides transparent access to the inner value. This also guards against future refactors silently breaking the hash-carrying invariant.

source: ['claude', 'codex']

🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-sdk/src/platform/transition/state_transition_result.rs`:
- [SUGGESTION] lines 1-56: No unit tests for StateTransitionResult<T>
  The new type has 6 public methods (`new`, `transition_hash`, `into_parts`, `into_inner`, `map`) plus a `Deref` impl, but no `#[cfg(test)]` module. While the type is simple, unit tests would lock the contract — especially that `into_parts()` returns both components, that `map` preserves the hash, and that `Deref` provides transparent access to the inner value. This also guards against future refactors silently breaking the hash-carrying invariant.

Comment on lines +50 to +56
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

💬 Nitpick: Deref impl on a non-smart-pointer type is unconventional

The Rust API guidelines recommend Deref only for smart pointers. StateTransitionResult<T> is a domain wrapper. The typical T here is StateTransitionProofResult, an enum that callers pattern-match on — but Deref does not enable pattern matching through the wrapper. All 28 call sites in this PR use into_inner(), so the Deref impl is currently unused. Consider removing it in favor of an explicit inner() accessor, or documenting why transparent deref is desired for future callers.

source: ['claude']

3 changes: 2 additions & 1 deletion packages/rs-sdk/src/platform/transition/top_up_address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ impl<S: Signer<PlatformAddress>> TopUpAddress<S> for AddressesWithBalances {
ensure_valid_state_transition_structure(&state_transition, sdk.version())?;
let st_result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
.await?;
.await?
.into_inner();
match st_result {
StateTransitionProofResult::VerifiedAddressInfos(address_infos) => {
let expected_addresses =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ impl TopUpIdentity for Identity {
None,
)?;
ensure_valid_state_transition_structure(&state_transition, sdk.version())?;
let identity: PartialIdentity = state_transition.broadcast_and_wait(sdk, settings).await?;
let identity: PartialIdentity = state_transition
.broadcast_and_wait::<PartialIdentity>(sdk, settings)
.await?
.into_inner();

identity
.balance
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ impl<S: Signer<PlatformAddress>> TopUpIdentityFromAddresses<S> for Identity {
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedIdentityWithAddressInfos(
identity,
Expand Down
6 changes: 4 additions & 2 deletions packages/rs-sdk/src/platform/transition/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,10 @@ impl TransferToIdentity for Identity {
)?;
ensure_valid_state_transition_structure(&state_transition, sdk.version())?;

let (sender, receiver): (PartialIdentity, PartialIdentity) =
state_transition.broadcast_and_wait(sdk, settings).await?;
let (sender, receiver): (PartialIdentity, PartialIdentity) = state_transition
.broadcast_and_wait::<(PartialIdentity, PartialIdentity)>(sdk, settings)
.await?
.into_inner();

let sender_balance = sender.balance.ok_or_else(|| {
Error::Generic("expected an identity balance after transfer (sender)".to_string())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ impl<S: Signer<PlatformAddress>> TransferAddressFunds<S> for Sdk {
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(self, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedAddressInfos(address_infos_map) => {
collect_address_infos_from_proof(address_infos_map, &expected_addresses)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ impl TransferToAddresses for Identity {
match state_transition
.broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
.await?
.into_inner()
{
StateTransitionProofResult::VerifiedIdentityWithAddressInfos(
identity,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ impl WithdrawFromIdentity for Identity {
)?;
ensure_valid_state_transition_structure(&state_transition, sdk.version())?;

let result = state_transition.broadcast_and_wait(sdk, settings).await?;
let result = state_transition
.broadcast_and_wait::<StateTransitionProofResult>(sdk, settings)
.await?
.into_inner();

match result {
StateTransitionProofResult::VerifiedPartialIdentity(identity) => {
Expand Down
2 changes: 1 addition & 1 deletion packages/wasm-sdk/src/state_transitions/broadcast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,6 @@ impl WasmSdk {
.await
.map_err(|e| WasmSdkError::generic(format!("Failed to broadcast: {}", e)))?;

convert_proof_result(result).map_err(WasmSdkError::from)
convert_proof_result(result.into_inner()).map_err(WasmSdkError::from)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

🟡 Suggestion: WASM broadcastAndWait discards the transition hash before crossing the FFI boundary

The WASM SDK's public broadcastAndWait calls result.into_inner() at line 99, permanently discarding the transition hash before converting to StateTransitionProofResultTypeJs. JavaScript consumers have no way to access the transition hash through this path. The same pattern exists in contract.rs:228-230 and identity.rs:691-694, where broadcast_and_wait is called and the entire StateTransitionResult is dropped.

If this is intentionally deferred to the #2953 follow-up, consider adding a brief // TODO(#2953): propagate transition_hash to JS comment so the gap is tracked alongside the code.

source: ['claude', 'codex']

🤖 Fix this with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/wasm-sdk/src/state_transitions/broadcast.rs`:
- [SUGGESTION] line 99: WASM broadcastAndWait discards the transition hash before crossing the FFI boundary
  The WASM SDK's public `broadcastAndWait` calls `result.into_inner()` at line 99, permanently discarding the transition hash before converting to `StateTransitionProofResultTypeJs`. JavaScript consumers have no way to access the transition hash through this path. The same pattern exists in `contract.rs:228-230` and `identity.rs:691-694`, where `broadcast_and_wait` is called and the entire `StateTransitionResult` is dropped.

If this is intentionally deferred to the #2953 follow-up, consider adding a brief `// TODO(#2953): propagate transition_hash to JS` comment so the gap is tracked alongside the code.

}
}
Loading