@exodus/ethereum-api 8.64.5 → 8.64.6

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 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.6](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.64.5...@exodus/ethereum-api@8.64.6) (2026-02-23)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+
12
+ * fix: avoid race conditions resulting in a `tipGasPrice` of `0` where possible (#7458)
13
+
14
+
15
+
6
16
  ## [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
17
 
8
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "8.64.5",
3
+ "version": "8.64.6",
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": "12e4eb3740319bb74eaedcc04b53c0dc056ad1a1"
70
+ "gitHead": "05a14c42a3963199e2b0b11d0a643884ebae8e46"
71
71
  }
@@ -6,6 +6,7 @@ import lodash from 'lodash'
6
6
 
7
7
  import { fromHexToString } from '../number-utils.js'
8
8
  import { errorMessageToSafeHint } from './errors.js'
9
+ import { getFallbackGasPriceEstimation } from './utils.js'
9
10
 
10
11
  const { isEmpty } = lodash
11
12
 
@@ -166,6 +167,10 @@ export default class ApiCoinNodesServer extends EventEmitter {
166
167
  return this.sendRequest(request)
167
168
  }
168
169
 
170
+ async getGasPriceEstimation() {
171
+ return getFallbackGasPriceEstimation({ server: this })
172
+ }
173
+
169
174
  // for fee monitor
170
175
  getGasPrice = this.gasPrice
171
176
 
@@ -6,6 +6,7 @@ import SolidityContract from '@exodus/solidity-contract'
6
6
  import ms from 'ms'
7
7
 
8
8
  import { fromHexToString } from '../number-utils.js'
9
+ import { getFallbackGasPriceEstimation } from './utils.js'
9
10
  import createWebSocket from './ws.js'
10
11
 
11
12
  const RETRY_DELAYS = ['10s']
@@ -120,6 +121,10 @@ export function create(defaultURL, ensAssetName) {
120
121
  return requestWithRetry('proxy', { method: 'eth_gasPrice' })
121
122
  },
122
123
 
124
+ async getGasPriceEstimation() {
125
+ return getFallbackGasPriceEstimation({ server: this })
126
+ },
127
+
123
128
  // for fee monitor
124
129
  async getGasPrice() {
125
130
  return requestWithRetry('proxy', { method: 'eth_gasPrice' })
@@ -6,6 +6,7 @@ import io from 'socket.io-client'
6
6
 
7
7
  import { fromHexToString } from '../number-utils.js'
8
8
  import { errorMessageToSafeHint } from './errors.js'
9
+ import { getFallbackGasPriceEstimation } from './utils.js'
9
10
 
10
11
  export const RPC_REQUEST_TIMEOUT = 'RPC_REQUEST_TIMEOUT'
11
12
 
@@ -174,6 +175,10 @@ export default class ClarityServer extends EventEmitter {
174
175
  return fee?.gasPrice
175
176
  }
176
177
 
178
+ async getGasPriceEstimation() {
179
+ return getFallbackGasPriceEstimation({ server: this })
180
+ }
181
+
177
182
  async sendRpcRequest(rpcRequest) {
178
183
  const rpcSocket = this.connectRpc()
179
184
  return new Promise((resolve, reject) => {
@@ -0,0 +1,31 @@
1
+ import assert from 'minimalistic-assert'
2
+
3
+ const desc = (a, b) => (a > b ? -1 : b > a ? 1 : 0)
4
+
5
+ export const getFallbackGasPriceEstimation = async ({ server }) => {
6
+ const [latestBlock, gasPrice] = await Promise.all([server.getLatestBlock(), server.getGasPrice()])
7
+
8
+ assert(latestBlock, 'expected latestBlock')
9
+ assert(gasPrice, 'expected gasPrice')
10
+
11
+ const baseFeePerGas = latestBlock.baseFeePerGas
12
+ if (!baseFeePerGas) return { gasPrice }
13
+
14
+ const [max, min] = [BigInt(gasPrice), BigInt(baseFeePerGas)].sort(desc)
15
+
16
+ const toHex = (b) => `0x${b.toString(16)}`
17
+
18
+ // TODO: Use `eth_feeHistory` or `eth_maxPriorityFeePerGas`
19
+ // instead (requires allowlist at the RPC).
20
+ // HACK: Infer the RPC's implicit `tipGasPrice`:
21
+ // https://github.com/ethereum/go-ethereum/blob/d3dd48e59db28ea04bd92e4337cdd488ccb8fbec/internal/ethapi/api.go#L69C1-L79C2
22
+ const maxPriorityFeePerGas50Percentile = max - min
23
+
24
+ const rewardPercentiles = {
25
+ 25: toHex(maxPriorityFeePerGas50Percentile / BigInt(2)),
26
+ 50: toHex(maxPriorityFeePerGas50Percentile),
27
+ 75: toHex((maxPriorityFeePerGas50Percentile * BigInt(3)) / BigInt(2)),
28
+ }
29
+
30
+ return { gasPrice, baseFeePerGas, rewardPercentiles }
31
+ }
package/src/fee-utils.js CHANGED
@@ -1,14 +1,5 @@
1
1
  import assert from 'minimalistic-assert'
2
2
 
3
- export const shouldFetchEthLikeFallbackGasPrices = async ({ eip1559Enabled, server }) => {
4
- const [gasPrice, baseFeePerGas] = await Promise.all([
5
- server.getGasPrice(),
6
- eip1559Enabled ? server.getBaseFeePerGas() : undefined,
7
- ])
8
-
9
- return { gasPrice, baseFeePerGas }
10
- }
11
-
12
3
  export const applyMultiplierToPrice = ({ feeAsset, gasPriceMultiplier, price }) => {
13
4
  assert(typeof price === 'string', 'price should be a string')
14
5
  return feeAsset.currency
@@ -1,10 +1,7 @@
1
1
  import { FeeMonitor } from '@exodus/asset-lib'
2
2
  import assert from 'minimalistic-assert'
3
3
 
4
- import {
5
- calculateEthLikeFeeMonitorUpdate,
6
- shouldFetchEthLikeFallbackGasPrices,
7
- } from './fee-utils.js'
4
+ import { calculateEthLikeFeeMonitorUpdate } from './fee-utils.js'
8
5
 
9
6
  /**
10
7
  * Generic eth server based fee monitor.
@@ -25,11 +22,6 @@ export const serverBasedFeeMonitorFactoryFactory = ({ asset, interval, server, a
25
22
  assert(server, 'server is required')
26
23
  assert(aci, 'aci is required')
27
24
 
28
- const shouldFetchGasPrices = async () => {
29
- const { eip1559Enabled } = await aci.getFeeConfig({ assetName: asset.name })
30
- return shouldFetchEthLikeFallbackGasPrices({ eip1559Enabled, server })
31
- }
32
-
33
25
  const FeeMonitorClass = class ServerBaseEthereumFeeMonitor extends FeeMonitor {
34
26
  constructor({ updateFee }) {
35
27
  assert(updateFee, 'updateFee is required')
@@ -44,7 +36,7 @@ export const serverBasedFeeMonitorFactoryFactory = ({ asset, interval, server, a
44
36
  return calculateEthLikeFeeMonitorUpdate({
45
37
  assetClientInterface: aci,
46
38
  feeAsset: asset,
47
- fetchedGasPrices: await shouldFetchGasPrices(),
39
+ fetchedGasPrices: await server.getGasPriceEstimation(),
48
40
  })
49
41
  }
50
42
  }