@exodus/ethereum-api 8.64.4 → 8.64.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/package.json +2 -2
- package/src/tx-log/clarity-monitor-v2.js +13 -2
- package/src/tx-log/clarity-monitor.js +13 -3
- package/src/tx-log/clarity-utils/index.js +1 -0
- package/src/tx-log/clarity-utils/normalize-transactions-response.js +59 -0
- package/src/tx-send/nonce-utils.js +4 -7
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [8.64.5](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.64.4...@exodus/ethereum-api@8.64.5) (2026-02-11)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* fix: future transaction replaces pending evm transactions (#7406)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
6
16
|
## [8.64.4](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.64.3...@exodus/ethereum-api@8.64.4) (2026-02-09)
|
|
7
17
|
|
|
8
18
|
**Note:** Version bump only for package @exodus/ethereum-api
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "8.64.
|
|
3
|
+
"version": "8.64.5",
|
|
4
4
|
"description": "Transaction monitors, fee monitors, RPC with the blockchain node, and other networking code for Ethereum and EVM-based blockchains",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -67,5 +67,5 @@
|
|
|
67
67
|
"type": "git",
|
|
68
68
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
69
69
|
},
|
|
70
|
-
"gitHead": "
|
|
70
|
+
"gitHead": "12e4eb3740319bb74eaedcc04b53c0dc056ad1a1"
|
|
71
71
|
}
|
|
@@ -6,7 +6,11 @@ import assert from 'minimalistic-assert'
|
|
|
6
6
|
import WsGateway from '../exodus-eth-server/ws-gateway.js'
|
|
7
7
|
import { executeEthLikeFeeMonitorUpdate } from '../fee-utils.js'
|
|
8
8
|
import { fromHexToString } from '../number-utils.js'
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
filterEffects,
|
|
11
|
+
getLogItemsFromServerTx,
|
|
12
|
+
normalizeTransactionsResponse,
|
|
13
|
+
} from './clarity-utils/index.js'
|
|
10
14
|
import {
|
|
11
15
|
checkPendingTransactions,
|
|
12
16
|
excludeUnchangedTokenBalances,
|
|
@@ -160,7 +164,14 @@ export class ClarityMonitorV2 extends BaseMonitor {
|
|
|
160
164
|
const { derivedData, tokensByAddress, assets, tokens, assetName } = walletAccountInfo
|
|
161
165
|
|
|
162
166
|
const response = await this.getHistoryFromServer({ walletAccount, derivedData, refresh })
|
|
163
|
-
|
|
167
|
+
|
|
168
|
+
const { allTxs } = await normalizeTransactionsResponse({
|
|
169
|
+
asset: this.asset,
|
|
170
|
+
fromAddress: derivedData.ourWalletAddress,
|
|
171
|
+
response,
|
|
172
|
+
walletAccount,
|
|
173
|
+
})
|
|
174
|
+
|
|
164
175
|
const cursor = response.cursor
|
|
165
176
|
|
|
166
177
|
await this.processAndFillTransactionsToState({
|
|
@@ -5,7 +5,11 @@ import assert from 'minimalistic-assert'
|
|
|
5
5
|
|
|
6
6
|
import { executeEthLikeFeeMonitorUpdate } from '../fee-utils.js'
|
|
7
7
|
import { fromHexToString } from '../number-utils.js'
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
filterEffects,
|
|
10
|
+
getLogItemsFromServerTx,
|
|
11
|
+
normalizeTransactionsResponse,
|
|
12
|
+
} from './clarity-utils/index.js'
|
|
9
13
|
import {
|
|
10
14
|
checkPendingTransactions,
|
|
11
15
|
excludeUnchangedTokenBalances,
|
|
@@ -148,10 +152,16 @@ export class ClarityMonitor extends BaseMonitor {
|
|
|
148
152
|
return map
|
|
149
153
|
}, new Map())
|
|
150
154
|
const assetName = this.asset.name
|
|
151
|
-
|
|
152
155
|
const derivedData = await this.deriveData({ assetName, walletAccount, tokens })
|
|
153
156
|
const response = await this.getHistoryFromServer({ walletAccount, derivedData, refresh })
|
|
154
|
-
|
|
157
|
+
|
|
158
|
+
const { allTxs } = await normalizeTransactionsResponse({
|
|
159
|
+
asset: this.asset,
|
|
160
|
+
fromAddress: derivedData.ourWalletAddress,
|
|
161
|
+
response,
|
|
162
|
+
walletAccount,
|
|
163
|
+
})
|
|
164
|
+
|
|
155
165
|
const hasNewTxs = allTxs.length > 0
|
|
156
166
|
|
|
157
167
|
const logItemsByAsset = this.getAllLogItemsByAsset({
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import assert from 'minimalistic-assert'
|
|
2
|
+
|
|
3
|
+
// Converts the `pending` and `confirmed` transactions returned
|
|
4
|
+
// by Clarity into a single contiguous array of transactions.
|
|
5
|
+
//
|
|
6
|
+
// Since it is possible for Clarity to return very old pending
|
|
7
|
+
// transactions that our own RPC has forgotten (see `--txpool.lifetime`),
|
|
8
|
+
// we ensure that all transactions we expose to the consumer have
|
|
9
|
+
// `nonce`s do not exceed the maximum pending nonce that's currently
|
|
10
|
+
// maintained at the RPC.
|
|
11
|
+
//
|
|
12
|
+
// This avoids the "future transaction replaces pending" error.
|
|
13
|
+
export const normalizeTransactionsResponse = async ({
|
|
14
|
+
asset,
|
|
15
|
+
fromAddress,
|
|
16
|
+
response,
|
|
17
|
+
walletAccount,
|
|
18
|
+
}) => {
|
|
19
|
+
assert(asset, 'expected asset')
|
|
20
|
+
assert(fromAddress, 'expected fromAddress')
|
|
21
|
+
assert(response, 'expected response')
|
|
22
|
+
assert(walletAccount, 'expected walletAccount')
|
|
23
|
+
|
|
24
|
+
const { baseAsset } = asset
|
|
25
|
+
|
|
26
|
+
// NOTE: We query the current pending `nonce` from the RPC
|
|
27
|
+
// to sanity check the contents of the `txLog`.
|
|
28
|
+
const pendingNonce = await baseAsset
|
|
29
|
+
.getNonce({
|
|
30
|
+
asset: baseAsset,
|
|
31
|
+
fromAddress,
|
|
32
|
+
walletAccount,
|
|
33
|
+
forceFromNode: true,
|
|
34
|
+
})
|
|
35
|
+
.catch(() => null)
|
|
36
|
+
|
|
37
|
+
const allTxs = [...response.transactions.pending, ...response.transactions.confirmed].filter(
|
|
38
|
+
(tx) => {
|
|
39
|
+
// If the transaction isn't one we've sent, then ignore.
|
|
40
|
+
if (tx.from?.toLowerCase() !== fromAddress.toLowerCase()) return true
|
|
41
|
+
|
|
42
|
+
const nonce = parseInt(tx.nonce, 10)
|
|
43
|
+
if (!Number.isFinite(nonce) || !Number.isInteger(nonce)) return false
|
|
44
|
+
|
|
45
|
+
// HACK: If we were unable to determine the `pendingNonce`, then
|
|
46
|
+
// allow send transactions through by default without
|
|
47
|
+
// further validation.
|
|
48
|
+
if (pendingNonce === null) return true
|
|
49
|
+
|
|
50
|
+
// NOTE: If the `tx` response contains pending transactions at
|
|
51
|
+
// a nonce offset that's greater than what's considered
|
|
52
|
+
// pending at the RPC, these transactions are no longer
|
|
53
|
+
// viable and should be replaced.
|
|
54
|
+
return nonce < pendingNonce
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
return { allTxs }
|
|
59
|
+
}
|
|
@@ -21,14 +21,11 @@ export const resolveNonce = async ({
|
|
|
21
21
|
}) => {
|
|
22
22
|
assertValidBlockTag(tag)
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
asset.baseAsset
|
|
26
|
-
|
|
27
|
-
: 0
|
|
28
|
-
|
|
29
|
-
const nonceFromLog = getNonceFromTxLog({ txLog, useAbsoluteNonce, tag })
|
|
24
|
+
if (asset.baseAsset?.api?.features?.noHistory || forceFromNode) {
|
|
25
|
+
return getNonce({ asset: asset.baseAsset, address: fromAddress, tag })
|
|
26
|
+
}
|
|
30
27
|
|
|
31
|
-
return
|
|
28
|
+
return getNonceFromTxLog({ txLog, useAbsoluteNonce, tag })
|
|
32
29
|
}
|
|
33
30
|
|
|
34
31
|
const getLatestTxWithNonceChange = ({ reversedTxLog }) => {
|