From a233c0d1389f4aaa244f162570e9ae2570619bd0 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 6 Aug 2025 19:01:58 -0400 Subject: [PATCH 1/8] refresh EVM nonces on publish --- .../bitcore-wallet-service/src/lib/chain/eth/index.ts | 9 +++++++-- packages/bitcore-wallet-service/src/lib/server.ts | 8 +++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts index 449acb103b6..ece1a51a1d8 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts @@ -649,7 +649,12 @@ export class EthChain implements IChain { return cb(null, 0); } - refreshTxData(_server: WalletService, txp, _opts, cb) { - return cb(null, txp); + refreshTxData(server: WalletService, txp, opts, cb) { + // set latest nonce + server._getTransactionCount(opts.wallet, txp.from, (err, nonce) => { + if (err) return cb(err); + txp.nonce = nonce; + return cb(null, txp); + }); } } diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index 455a2872065..552b9c48e4b 100644 --- a/packages/bitcore-wallet-service/src/lib/server.ts +++ b/packages/bitcore-wallet-service/src/lib/server.ts @@ -2655,6 +2655,10 @@ export class WalletService implements IWalletService { // SOL is skipped since its a non necessary field that is expected to be provided by the client. if (!opts.nonce && !Constants.SVM_CHAINS[wallet.chain.toUpperCase()]) { try { + if (Constants.EVM_CHAINS[wallet.chain.toUpperCase()]) { + // if nonce mangement is done by BWS, default to refreshing nonce on txp publish + opts.refreshOnPublish = true; + } opts.nonce = await ChainService.getTransactionCount(this, wallet, opts.from); } catch (error) { return next(error); @@ -2897,6 +2901,7 @@ export class WalletService implements IWalletService { ChainService.checkTxUTXOs(this, txp, opts, err => { if (err) return cb(err); txp.status = 'pending'; + opts.wallet = wallet; ChainService.refreshTxData(this, txp, opts, (err, txp) => { if (err) return cb(err); if (txp.isRepublishEnabled() && !txp.prePublishRaw) { @@ -3162,7 +3167,8 @@ export class WalletService implements IWalletService { if (txp.signingMethod === 'schnorr' && !opts.supportBchSchnorr) return cb(Errors.UPGRADE_NEEDED); - if ([...Object.keys(Constants.EVM_CHAINS), 'XRP'].includes(wallet.chain.toUpperCase())) { + // Validate nonces only if they are not reset on signing via a re-publish + if ([...Object.keys(Constants.EVM_CHAINS), 'XRP'].includes(wallet.chain.toUpperCase()) && !txp.isRepublishEnabled()) { try { const txps = await this.getPendingTxsPromise({}); for (let t of txps) { From 054f4e218f4d6a63cbd45f3e25041435c7b6ede8 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 6 Aug 2025 19:10:38 -0400 Subject: [PATCH 2/8] validate nonce --- packages/bitcore-wallet-service/src/lib/chain/eth/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts index ece1a51a1d8..8556af417a5 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts @@ -653,6 +653,9 @@ export class EthChain implements IChain { // set latest nonce server._getTransactionCount(opts.wallet, txp.from, (err, nonce) => { if (err) return cb(err); + if (!Number(nonce)) { + return cb(new Error('Nonce is not a number')); + } txp.nonce = nonce; return cb(null, txp); }); From 67132d4dcdee2f667662f4af4825f3d789da575c Mon Sep 17 00:00:00 2001 From: lyambo Date: Tue, 26 Aug 2025 18:38:03 -0400 Subject: [PATCH 3/8] fix notifcation type --- packages/bitcore-wallet-service/src/lib/server.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index 552b9c48e4b..26003e7cdff 100644 --- a/packages/bitcore-wallet-service/src/lib/server.ts +++ b/packages/bitcore-wallet-service/src/lib/server.ts @@ -2872,6 +2872,7 @@ export class WalletService implements IWalletService { if (!txp.isTemporary() && !txp.isRepublishEnabled()) return cb(null, txp); const copayer = wallet.getCopayer(this.copayerId); + const initialStatus = txp.status; let raw; try { @@ -2910,7 +2911,8 @@ export class WalletService implements IWalletService { } this.storage.storeTx(this.walletId, txp, err => { if (err) return cb(err); - const action = txp.isRepublishEnabled() && txp.prePublishRaw ? 'UpdatedTxProposal' : 'NewTxProposal'; + + const action = initialStatus === 'pending' ? 'UpdatedTxProposal' : 'NewTxProposal'; this._notifyTxProposalAction(action, txp, () => { if (txp.coin == 'bch' && txp.changeAddress) { const format = opts.noCashAddr ? 'copay' : 'cashaddr'; From 054c37d58a99ca148dfca7fcf817fc0cda5e6a8d Mon Sep 17 00:00:00 2001 From: lyambo Date: Tue, 26 Aug 2025 18:48:30 -0400 Subject: [PATCH 4/8] check for nonce conflicts with non refresh on publish transactions --- packages/bitcore-wallet-service/src/lib/server.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index 26003e7cdff..e45c7997296 100644 --- a/packages/bitcore-wallet-service/src/lib/server.ts +++ b/packages/bitcore-wallet-service/src/lib/server.ts @@ -3170,11 +3170,11 @@ export class WalletService implements IWalletService { if (txp.signingMethod === 'schnorr' && !opts.supportBchSchnorr) return cb(Errors.UPGRADE_NEEDED); // Validate nonces only if they are not reset on signing via a re-publish - if ([...Object.keys(Constants.EVM_CHAINS), 'XRP'].includes(wallet.chain.toUpperCase()) && !txp.isRepublishEnabled()) { + if ([...Object.keys(Constants.EVM_CHAINS), 'XRP'].includes(wallet.chain.toUpperCase())) { try { const txps = await this.getPendingTxsPromise({}); for (let t of txps) { - if (t.id !== txp.id && t.nonce <= txp.nonce && t.status !== 'rejected') { + if (t.id !== txp.id && t.nonce <= txp.nonce && t.status !== 'rejected' && !t.isRepublishEnabled()) { return cb(Errors.TX_NONCE_CONFLICT); } } From 9ac9e2aa69dc791de35361be8483a9f3c909645e Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 27 Aug 2025 09:36:07 -0400 Subject: [PATCH 5/8] ci testing --- packages/bitcore-wallet-client/test/helpers.js | 1 + packages/bitcore-wallet-service/src/lib/chain/eth/index.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/bitcore-wallet-client/test/helpers.js b/packages/bitcore-wallet-client/test/helpers.js index d692b053cb8..fe375596b99 100644 --- a/packages/bitcore-wallet-client/test/helpers.js +++ b/packages/bitcore-wallet-client/test/helpers.js @@ -352,6 +352,7 @@ const blockchainExplorerMock = { }); }, getTransactionCount: (addr, cb) => { + console.log(' using mock getTransactionCount') return cb(null, 0); }, reset: () => { diff --git a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts index 8556af417a5..ad848aab228 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts @@ -652,6 +652,7 @@ export class EthChain implements IChain { refreshTxData(server: WalletService, txp, opts, cb) { // set latest nonce server._getTransactionCount(opts.wallet, txp.from, (err, nonce) => { + console.log('nonce: ', nonce); if (err) return cb(err); if (!Number(nonce)) { return cb(new Error('Nonce is not a number')); From 234c78f57f44c0d56bb445832c99a8baeab5b016 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 27 Aug 2025 10:33:03 -0400 Subject: [PATCH 6/8] add logging --- packages/bitcore-wallet-service/src/lib/chain/eth/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts index ad848aab228..4700feff9fe 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts @@ -653,9 +653,11 @@ export class EthChain implements IChain { // set latest nonce server._getTransactionCount(opts.wallet, txp.from, (err, nonce) => { console.log('nonce: ', nonce); + console.log('err ', err) if (err) return cb(err); if (!Number(nonce)) { - return cb(new Error('Nonce is not a number')); + console.log('!Number(nonce)', !Number(nonce)) + return cb(new ClientError('Nonce is not a number', `Nonce is not a number. Found ${nonce}`)); } txp.nonce = nonce; return cb(null, txp); From 636be12c0ff2700e3f6233d849fbebaadfc60211 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 27 Aug 2025 10:52:28 -0400 Subject: [PATCH 7/8] add invalid nonce error definitions --- packages/bitcore-wallet-client/test/helpers.js | 1 - packages/bitcore-wallet-service/src/lib/chain/eth/index.ts | 7 ++----- .../src/lib/errors/errordefinitions.ts | 2 ++ 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/bitcore-wallet-client/test/helpers.js b/packages/bitcore-wallet-client/test/helpers.js index fe375596b99..d692b053cb8 100644 --- a/packages/bitcore-wallet-client/test/helpers.js +++ b/packages/bitcore-wallet-client/test/helpers.js @@ -352,7 +352,6 @@ const blockchainExplorerMock = { }); }, getTransactionCount: (addr, cb) => { - console.log(' using mock getTransactionCount') return cb(null, 0); }, reset: () => { diff --git a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts index 4700feff9fe..dbd09b015db 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts @@ -652,12 +652,9 @@ export class EthChain implements IChain { refreshTxData(server: WalletService, txp, opts, cb) { // set latest nonce server._getTransactionCount(opts.wallet, txp.from, (err, nonce) => { - console.log('nonce: ', nonce); - console.log('err ', err) if (err) return cb(err); - if (!Number(nonce)) { - console.log('!Number(nonce)', !Number(nonce)) - return cb(new ClientError('Nonce is not a number', `Nonce is not a number. Found ${nonce}`)); + if (!Number.isInteger(Number(0))) { + return cb(new ClientError(Errors.codes.INVALID_NONCE, `Nonce is not a number. Found ${nonce}`)); } txp.nonce = nonce; return cb(null, txp); diff --git a/packages/bitcore-wallet-service/src/lib/errors/errordefinitions.ts b/packages/bitcore-wallet-service/src/lib/errors/errordefinitions.ts index 69bae61de4f..664a0036e63 100644 --- a/packages/bitcore-wallet-service/src/lib/errors/errordefinitions.ts +++ b/packages/bitcore-wallet-service/src/lib/errors/errordefinitions.ts @@ -17,6 +17,7 @@ interface Errors { INSUFFICIENT_FUNDS_FOR_FEE: T; INVALID_ADDRESS: T; INVALID_CHANGE_ADDRESS: T; + INVALID_NONCE: T; KEY_IN_COPAYER: T; LOCKED_FUNDS: T; // Polygon Errors @@ -89,6 +90,7 @@ const errors: Errors = { LOCKED_OP_FEE: 'Your linked OP wallet does not have enough ETH for fee', INVALID_ADDRESS: 'Invalid address', INVALID_CHANGE_ADDRESS: 'Invalid change address', + INVALID_NONCE: 'Invalid nonce', KEY_IN_COPAYER: 'Key already registered', LOCKED_FUNDS: 'Funds are locked by pending transaction proposals', HISTORY_LIMIT_EXCEEDED: 'Requested page limit is above allowed maximum', From b439a254e761bc7a083947d71bc89427d4bebf51 Mon Sep 17 00:00:00 2001 From: lyambo Date: Wed, 27 Aug 2025 18:44:54 -0400 Subject: [PATCH 8/8] cleanup --- packages/bitcore-wallet-service/src/lib/chain/eth/index.ts | 5 +++-- packages/bitcore-wallet-service/src/lib/server.ts | 1 - 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts index dbd09b015db..411c8250528 100644 --- a/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts +++ b/packages/bitcore-wallet-service/src/lib/chain/eth/index.ts @@ -653,8 +653,9 @@ export class EthChain implements IChain { // set latest nonce server._getTransactionCount(opts.wallet, txp.from, (err, nonce) => { if (err) return cb(err); - if (!Number.isInteger(Number(0))) { - return cb(new ClientError(Errors.codes.INVALID_NONCE, `Nonce is not a number. Found ${nonce}`)); + nonce = Number(nonce); + if (!Number.isInteger(nonce)) { + return cb(new ClientError(Errors.codes.INVALID_NONCE, `Nonce is not a number. Found ${ nonce }`)); } txp.nonce = nonce; return cb(null, txp); diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index e45c7997296..e6dc37bd622 100644 --- a/packages/bitcore-wallet-service/src/lib/server.ts +++ b/packages/bitcore-wallet-service/src/lib/server.ts @@ -3169,7 +3169,6 @@ export class WalletService implements IWalletService { if (txp.signingMethod === 'schnorr' && !opts.supportBchSchnorr) return cb(Errors.UPGRADE_NEEDED); - // Validate nonces only if they are not reset on signing via a re-publish if ([...Object.keys(Constants.EVM_CHAINS), 'XRP'].includes(wallet.chain.toUpperCase())) { try { const txps = await this.getPendingTxsPromise({});