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..411c8250528 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,16 @@ 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); + 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/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', diff --git a/packages/bitcore-wallet-service/src/lib/server.ts b/packages/bitcore-wallet-service/src/lib/server.ts index 455a2872065..e6dc37bd622 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); @@ -2868,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 { @@ -2897,6 +2902,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) { @@ -2905,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'; @@ -3166,7 +3173,7 @@ export class WalletService implements IWalletService { 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); } }