Hey Klever Team,
When I am trying to process 2 or more transactions simultaneously (or with delay of about 2-3 seconds), both transactions are processed and I am getting successful status, but in reality one of transactions are not processed and I can’t find it in the blockchain. Here is my code:
const newAccount = new Account(privateKey);
await newAccount.ready;
// Build unsigned transaction
const unsignedTx = await newAccount.buildTransaction([{
payload,
type: TransactionType.Transfer,
}]);
// Sign transaction
const signedTx = await newAccount.signTransaction(unsignedTx);
// Broadcast transaction
const broadcastRes = await newAccount.broadcastTransactions([signedTx]);
console.log(broadcastRes) /** {
"data": {
"txsHashes": [
“…”
]
},
"error": "",
"code": "successful",
} for both transactions
*/
I am using latest version (2.4.1) of sdk-node.
Can you help me pinpoint the issue?
1 Like
Hello @shymelon,
When you broadcast a transaction and receive a success response, it only means the transaction has been accepted into the mempool (i.e., it’s queued to be included in a future block — which, in this case, is produced every 4 seconds). However, being in the mempool doesn’t guarantee the transaction will be included in a block. The node may still reject it if certain criteria aren’t met.
One of the most common reasons for a transaction being dropped is an incorrect nonce.
For example, if your account is currently at nonce 100
and you create two transactions simultaneously, both might be assigned nonce 100
. Initially, the blockchain accepts both into the queue because the nonce is valid at that moment. But after the first transaction is processed, your account’s nonce becomes 101
. The second transaction, still with nonce 100
, is now invalid and gets dropped.
Debugging Steps:
I suggest logging the unsignedTx
object to the console. It should display the nonce for each transaction. If both transactions show the same nonce, you’ve found your issue.
Recommended Solution:
Broadcast the first transaction. Then run await newAccount.sync()
to update the local account state and fetch the latest nonce. After that, build and broadcast the second transaction using the updated nonce.
If that doesn’t resolve the issue, feel free to share more details so we can troubleshoot together:
- Which network are you using — mainnet or testnet?
- What’s your wallet address? (I can check if there are any pending transactions stuck in the mempool.)
- Can you share the transaction hashes generated by your code? Ideally, they should be different.
- If possible, please also provide more context on how you’re executing the code snippet you posted. I’d like to better understand how the simultaneous transaction scenario is being triggered.
4 Likes
Hey Nicollas,
Thanks for detailed answer! Sync did not help sadly, here is example of hash 9e9f1eb9d79b92a3fe651fd6766f4ce6ed461a1dc6df0ee6bcf35cba9f4e13e2
(mainnet). My current setup and what I am doing:
- Code runs on node js, requests are done through API. Scenario which I am testing is making many requests at the same time.
- I have a mutex mechanism, so critical requests are done one by one with locking. There is a very small delay though, so nonces are indeed the same as you suspected, even after sync. Hashes are different.
Can you please advice me what can be done to mitigate that issue?
Thanks for the additional details, @shymelon.
You’ve identified the root cause: duplicate nonces. Since account.sync
isn’t resolving the issue, the best approach is to manually manage the nonce.
Manual nonce management is a technique we use for batch operations and asynchronous transaction broadcasting, which should suit your use case perfectly.
The approach is straightforward: Instead of letting the nodes determine the transaction nonce, you provide one during transaction creation. The tradeoff is that you’ll need to track the nonce in your system to maintain consistency.
Since you’re already using mutex, I recommend implementing manual nonce management within the mutex as well to prevent duplicate nonces.
Implementation: Your SDK node code should look like this:
const options = {
nonce: 100, // <- Insert your nonce here
};
const unsignedTx = await newAccount.buildTransaction([{
payload,
type: TransactionType.Transfer,
}], undefined, options);
Important note: When your application starts, you’ll still need to fetch the current nonce from the blockchain to determine the starting point. From there, simply increment the nonce for each transaction you build.
3 Likes
Thanks, I’ll give that a try and get back once I’ve tested it!