-
Notifications
You must be signed in to change notification settings - Fork 45
Description
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).