Skip to content

Bug: identity_create_from_addresses sends current nonce instead of next nonce (missing +1) #3083

@thephez

Description

@thephez

Summary

identityCreateFromAddresses in the WASM SDK sends the current address nonce to the platform, but the platform expects current + 1. This causes the state transition to be rejected with a nonce mismatch error.

Affected versions: Current release (3.0) and v3.1-dev branch.

Root Cause

In packages/wasm-sdk/src/state_transitions/addresses.rs (line 982 on v3.1-dev), the fetch_nonces_into_address_map function fetches the nonce and uses it directly without incrementing:

let nonce = info_received.nonce;  // current nonce — should be nonce + 1
Ok((address_requested, (nonce, amount)))

Every other address-based state transition in the rs-sdk correctly increments the nonce using the nonce_inc() utility (packages/rs-sdk/src/platform/transition/address_inputs.rs:34-40):

pub(crate) fn nonce_inc(
    data: BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
) -> BTreeMap<PlatformAddress, (AddressNonce, Credits)> {
    data.into_iter()
        .map(|(address, (nonce, credits))| (address, (nonce + 1, credits)))
        .collect()
}

Comparison with working methods

Method File Uses nonce_inc?
transfer_address_funds rs-sdk/.../transfer_address_funds.rs:55 ✅ Yes
top_up_from_addresses rs-sdk/.../top_up_identity_from_addresses.rs:54 ✅ Yes
withdraw_address_funds rs-sdk/.../address_credit_withdrawal.rs ✅ Yes
identity_create_from_addresses wasm-sdk/.../addresses.rs ❌ No

All working methods follow the pattern nonce_inc(fetch_inputs_with_nonce(...)). The identity_create_from_addresses WASM binding uses a custom fetch_nonces_into_address_map function that skips the increment.

Suggested Fix

In packages/wasm-sdk/src/state_transitions/addresses.rs, change:

let nonce = info_received.nonce;

to:

// Platform expects the next nonce (current + 1), matching the pattern
// used by transfer_address_funds, top_up_from_addresses, etc.
let nonce = info_received.nonce + 1;

Test Coverage Gap

The server-side tests in rs-drive-abci correctly validate nonce behavior — they set up addresses with nonce N and send transitions with nonce N+1 (e.g., tests.rs:1005 uses (1 as AddressNonce, input_amount) for an address initialized at nonce 0). The test_verify_nonce_incremented_after_execution test explicitly uses initial_nonce + 1.

However, no test exercises the WASM SDK's fetch_nonces_into_address_map function against a real or mocked platform to verify it sends fetched_nonce + 1. The JS-level test in addresses.spec.mjs:238-263 only stubs the WASM function and checks the stub was called — it never runs the actual Rust nonce logic.

This means:

  • Server-side tests prove the platform requires nonce + 1
  • No client-side test verifies the WASM SDK actually sends nonce + 1

An integration test that runs identity_create_from_addresses end-to-end (or at minimum a unit test of fetch_nonces_into_address_map that asserts the output nonce equals fetched_nonce + 1) would have caught this bug.

Impact

This bug prevents creating identities via identityCreateFromAddresses / createIdentity using address funding in the WASM SDK. The transaction is always rejected because it sends a stale nonce.


🤖 This issue was identified and written with the assistance of Claude Code (claude-opus-4-6).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions