Open
Conversation
EVM nonces assigned at txp creation time go stale when proposals sit waiting for signing. Add an opt-in deferNonce boolean so callers can signal that nonce assignment should happen later. Field added to ITxProposal interface, TxProposal class, create(), and fromObj() for persistence round-tripping.
When deferNonce is true, skip the getTransactionCount call during createTx and pass the flag through to TxProposal.create() so it persists on the proposal object.
Allow deferred-nonce txps to be published and re-published. Use prePublishRaw to store the original unsigned tx so proposal signature verification still works after nonce is assigned later.
Deferred-nonce proposals have nonce=null until assignNonce is called. Without null guards, the nonce comparison would coerce null to 0 and falsely trigger TX_NONCE_CONFLICT.
New endpoint lets the client request a fresh nonce just before signing. The handler fetches the confirmed nonce from the blockchain, skips past any pending proposal nonces in the database, and stores the gap-free result on the txp. Runs under _runLocked to prevent concurrent calls from receiving the same nonce for the same wallet.
Calls POST /v1/txproposals/{id}/assign-nonce/ so the app can
request a fresh nonce from BWS just before signing a
deferred-nonce transaction proposal.
Cover the full lifecycle: createTx with deferNonce, publishTx with prePublishRaw, assignNonce with gap-free calculation, signTx without false nonce conflicts, and broadcast. Includes a bulk-sign scenario that assigns sequential nonces to three deferred proposals signed one after another.
The repeated isRepublishEnabled() || deferNonce pattern in publishTx exists because both Solana blockhash refresh and EVM deferred nonce share the same need: preserve prePublishRaw for signature verification after the raw tx changes. Also remove unnecessary spread-copy before sorting pendingNonces in assignNonce since filter/map already produces a new array.
Consolidate into the existing integration test file rather than maintaining a separate file for one feature. Tests are placed under a new #assignNonce describe block between #signTx and #broadcastTx to match the transaction lifecycle order.
Number(undefined) produces NaN which BigInt cannot convert. Use 0 as placeholder when nonce is null so checkTx can still build the transaction during createTx. Also reset the getTransactionCount stub in test beforeEach to prevent cross-test contamination.
stubBroadcast must be called after signTx with the actual txid so the returned txid matches txp.txid. Tests that sign multiple txps need to broadcast each one before signing the next, otherwise the accepted txp blocks with TX_NONCE_CONFLICT.
The getTransactionCount stub is static so broadcasting resets the nonce calculation. The gap-free logic already handles pending txps so sequential assignNonce calls produce correct nonces without needing to sign and broadcast each one.
kajoseph
reviewed
Mar 19, 2026
| txp: Txp; | ||
| }, | ||
| /** @deprecated */ | ||
| cb?: (err?: Error, txp?: Txp) => void |
Collaborator
There was a problem hiding this comment.
This is a brand new function, so no need to support legacy implementations. Let's remove the callback
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
EVM nonces assigned at
createTxtime go stale when proposals sit pending. This adds adeferNonceoption that skips the nonce fetch during creation and instead assigns a fresh gap-free nonce via a newassignNonceendpoint, called by the client right before signing.Companion PRs needed for full end-to-end testing:
Changelog
POST /v1/txproposals/:id/assign-nonce/endpoint that fetches the confirmed nonce from chain, skips past any pending txp nonces, and stores the result on the txpcreateTxskips the nonce fetch whendeferNonce: true, andsignTxno longer flags nonce conflicts on null-nonce txpshasMutableTxData()method on TxProposal consolidates the publish-time guards for both Solana republish and deferred-nonce flowsassignNonce()client method added to BWCTesting Notes
Integration tests are in
server.test.tsunder#assignNonce (deferred nonce). Run with:For full E2E, you'll need the companion app and server PRs linked above. Create an ETH txp with
deferNonce: true, publish it, callassignNonce, then sign.Checklist
BWCif modifying the bitcore-wallet-client package,CLIif modifying the bitcore-cli package, etc.)