@exodus/ethereum-api 8.46.0 → 8.47.0

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,26 @@
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.47.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.46.1...@exodus/ethereum-api@8.47.0) (2025-09-08)
7
+
8
+
9
+ ### Features
10
+
11
+
12
+ * feat: add `EthLikeFeeData` to respect `min`, `max` and `multiplier` for EIP-1559 transactions (#6254)
13
+
14
+
15
+
16
+ ## [8.46.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.46.0...@exodus/ethereum-api@8.46.1) (2025-09-03)
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+
22
+ * fix: tx-create simplification (#6310)
23
+
24
+
25
+
6
26
  ## [8.46.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.45.6...@exodus/ethereum-api@8.46.0) (2025-08-21)
7
27
 
8
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "8.46.0",
3
+ "version": "8.47.0",
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",
@@ -64,5 +64,5 @@
64
64
  "type": "git",
65
65
  "url": "git+https://github.com/ExodusMovement/assets.git"
66
66
  },
67
- "gitHead": "9d7bd1606fa42f6e05c68e0396d5aed7f7ea8852"
67
+ "gitHead": "e0dac028c1eb8ad919ce437b88f7f8549eea6f88"
68
68
  }
@@ -29,11 +29,10 @@ import {
29
29
  import { createTokenFactory } from './create-token-factory.js'
30
30
  import { createCustomFeesApi } from './custom-fees.js'
31
31
  import { createEvmServer } from './exodus-eth-server/index.js'
32
- import { createFeeData } from './fee-data-factory.js'
32
+ import { createFeeData } from './fee-data/index.js'
33
33
  import { createGetBalanceForAddress } from './get-balance-for-address.js'
34
34
  import { getBalancesFactory } from './get-balances.js'
35
35
  import { getFeeFactory } from './get-fee.js'
36
- import getFeeAsyncFactory from './get-fee-async.js'
37
36
  import { estimateL1DataFeeFactory, getL1GetFeeFactory } from './optimism-gas/index.js'
38
37
  import { serverBasedFeeMonitorFactoryFactory } from './server-based-fee-monitor.js'
39
38
  import { createStakingApi } from './staking-api.js'
@@ -258,7 +257,7 @@ export const createAssetFactory = ({
258
257
  getBalanceForAddress: createGetBalanceForAddress({ asset, server }),
259
258
  getConfirmationsNumber: () => confirmationsNumber,
260
259
  getDefaultAddressPath: () => defaultAddressPath,
261
- getFeeAsync: getFeeAsyncFactory({ assetClientInterface, createTx }),
260
+ getFeeAsync: createTx, // createTx alias, remove me when possible
262
261
  getFee,
263
262
  getFeeData: () => feeData,
264
263
  getKeyIdentifier: createGetKeyIdentifier({
@@ -0,0 +1,81 @@
1
+ import { FeeData } from '@exodus/asset-lib'
2
+ import {
3
+ mapCurrency as defaultMapCurrency,
4
+ modelToJSON,
5
+ } from '@exodus/asset-lib/src/utils/index.js'
6
+ import { isNumberUnit } from '@exodus/currency'
7
+ import lodash from 'lodash'
8
+
9
+ const { isEmpty, isEqual } = lodash
10
+
11
+ const PRE_EIP1559_UNIT_KEYS_GUARD = ['gasPrice']
12
+ const EIP1559_UNIT_KEYS_GUARD = ['gasPrice', 'baseFeePerGas']
13
+
14
+ export const bound = ({ val, multiplier, max, min }) => {
15
+ if (!isNumberUnit(val)) return val
16
+
17
+ if (multiplier) {
18
+ val = val.mul(multiplier)
19
+ }
20
+
21
+ if (max) {
22
+ val = val.lte(max) ? val : max
23
+ }
24
+
25
+ if (min) {
26
+ val = val.gte(min) ? val : min
27
+ }
28
+
29
+ return val
30
+ }
31
+
32
+ const mapCurrency = ({ currency, config }) => {
33
+ const { eip1559Enabled } = config
34
+ return defaultMapCurrency(
35
+ config,
36
+ [currency],
37
+ eip1559Enabled ? EIP1559_UNIT_KEYS_GUARD : PRE_EIP1559_UNIT_KEYS_GUARD
38
+ )
39
+ }
40
+
41
+ const boundFactory =
42
+ ({ config }) =>
43
+ (val) => {
44
+ const { multiplier, max, min } = config
45
+ return bound({ val, multiplier, max, min })
46
+ }
47
+
48
+ export class EthLikeFeeData extends FeeData {
49
+ constructor({ currency, config }) {
50
+ super({ currency, config, mainKey: 'gasPrice' })
51
+ Object.assign(this, mapCurrency({ currency, config }))
52
+ }
53
+
54
+ update(config = {}) {
55
+ if (isEmpty(config)) return this
56
+
57
+ config = super.update(config)
58
+
59
+ if (config.eip1559Enabled) {
60
+ const bound = boundFactory({ config })
61
+
62
+ const { baseFeePerGas, tipGasPrice = this.currency.ZERO } = config
63
+
64
+ const enforcedEffectiveGasPrice = bound(baseFeePerGas.add(tipGasPrice))
65
+
66
+ // When the `effectiveGasPrice` is lower, we'll increase the
67
+ // `tipGasPrice` to ensure we meet the `enforcedEffectiveGasPrice`.
68
+ //
69
+ // Conversely, when the `effectiveGasPrice` is too high, we'll
70
+ // try to reduce the `tipGasPrice` where possible, but whilst
71
+ // maintaining the `baseFeePerGas`, since this is the minimum
72
+ // for the current network.
73
+ Object.assign(config, {
74
+ tipGasPrice: enforcedEffectiveGasPrice.sub(baseFeePerGas).clampLowerZero(),
75
+ })
76
+ }
77
+
78
+ if (isEqual(this.toJSON(), modelToJSON({ ...config }))) return this
79
+ return new this.constructor({ config, currency: this.currency })
80
+ }
81
+ }
@@ -1,7 +1,8 @@
1
- import { FeeData } from '@exodus/asset-lib'
2
1
  import assert from 'minimalistic-assert'
3
2
 
4
- const createFeeDataConfigDefaults = ({ currency, feeDataConfig }) => {
3
+ import { EthLikeFeeData } from './EthLikeFeeData.js'
4
+
5
+ export const createFeeDataConfigDefaults = ({ currency, feeDataConfig }) => {
5
6
  const shared = {
6
7
  tipGasPrice: '0 Gwei',
7
8
  fuelThreshold: '0 Gwei',
@@ -48,9 +49,6 @@ const createFeeDataConfigDefaults = ({ currency, feeDataConfig }) => {
48
49
  }
49
50
 
50
51
  export const createFeeData = ({ currency, feeDataConfig }) => {
51
- return new FeeData({
52
- config: createFeeDataConfigDefaults({ currency, feeDataConfig }),
53
- mainKey: 'gasPrice',
54
- currency,
55
- })
52
+ const config = createFeeDataConfigDefaults({ currency, feeDataConfig })
53
+ return new EthLikeFeeData({ config, currency })
56
54
  }
package/src/tx-create.js CHANGED
@@ -6,7 +6,7 @@ import * as ErrorWrapper from './error-wrapper.js'
6
6
  import { isContractAddressCached } from './eth-like-util.js'
7
7
  import { ensureSaneEip1559GasPriceForTipGasPrice } from './fee-utils.js'
8
8
  import { ARBITRARY_ADDRESS, fetchGasLimit, resolveDefaultTxInput } from './gas-estimation.js'
9
- import { getFeeFactoryGasPrices } from './get-fee.js'
9
+ import { getExtraFeeData, getFeeFactoryGasPrices } from './get-fee.js'
10
10
  import { getNftArguments } from './nft-utils.js'
11
11
 
12
12
  async function createUnsignedTxWithFees({
@@ -66,7 +66,7 @@ async function createUnsignedTxWithFees({
66
66
  : asset.baseAsset.currency.ZERO
67
67
 
68
68
  const fee = baseFee.add(l1DataFee)
69
-
69
+ const extraFeeData = getExtraFeeData({ asset, amount: coinAmount })
70
70
  const unsignedTx = {
71
71
  txData: { transactionBuffer, chainId },
72
72
  txMeta: {
@@ -81,6 +81,8 @@ async function createUnsignedTxWithFees({
81
81
  }
82
82
  return {
83
83
  unsignedTx,
84
+ fee,
85
+ extraFeeData,
84
86
  // exhcange compatibility until the use usignedTx, remove me!
85
87
  gasPrice,
86
88
  tipGasPrice,
@@ -195,7 +197,6 @@ export const createTxFactory = ({ chainId, assetClientInterface, useAbsoluteNonc
195
197
  return async ({
196
198
  asset,
197
199
  walletAccount,
198
- feeData,
199
200
  nft, // when sending nfts
200
201
  fromAddress: providedFromAddress, // wallet from address
201
202
  toAddress: providedToAddress, // user's to address, not the token or the dex contract
@@ -213,8 +214,16 @@ export const createTxFactory = ({ chainId, assetClientInterface, useAbsoluteNonc
213
214
  keepTxInput, // @deprecated this flag is used by swaps when swapping a token via DEX. The asset is token but the tx TO address is not the token address. Update swap to use `contractAddress`
214
215
  }) => {
215
216
  assert(asset, 'asset is required')
216
- assert(feeData, 'feeData is required')
217
- const fromAddress = providedFromAddress || ARBITRARY_ADDRESS
217
+ assert(walletAccount, 'walletAccount is required')
218
+
219
+ const feeData = await assetClientInterface.getFeeConfig({ assetName: asset.baseAsset.name })
220
+
221
+ const fromAddress =
222
+ providedFromAddress ??
223
+ (await assetClientInterface.getReceiveAddress({
224
+ assetName: asset.baseAsset.name,
225
+ walletAccount,
226
+ }))
218
227
 
219
228
  const baseAssetTxLog = await assetClientInterface.getTxLog({
220
229
  assetName: asset.baseAsset.name,
@@ -320,7 +329,7 @@ export const createTxFactory = ({ chainId, assetClientInterface, useAbsoluteNonc
320
329
  (await fetchGasLimit({
321
330
  asset,
322
331
  feeData,
323
- fromAddress: providedFromAddress,
332
+ fromAddress,
324
333
  toAddress: providedToAddress,
325
334
  txInput: providedTxInput,
326
335
  contractAddress: txToAddress,
@@ -33,24 +33,9 @@ const txSendFactory = ({ assetClientInterface, createTx }) => {
33
33
  return { unsignedTx: providedUnsignedTx }
34
34
  }
35
35
 
36
- const feeData =
37
- legacyParams.feeData ??
38
- (await assetClientInterface.getFeeData({
39
- assetName: baseAsset.name,
40
- }))
41
-
42
- const fromAddress =
43
- legacyParams.fromAddress ??
44
- (await assetClientInterface.getReceiveAddress({
45
- assetName: baseAsset.name,
46
- walletAccount,
47
- }))
48
-
49
36
  return createTx({
50
37
  asset,
51
38
  walletAccount,
52
- feeData,
53
- fromAddress,
54
39
  toAddress: legacyParams.address,
55
40
  ...legacyParams,
56
41
  ...legacyParams.options,
@@ -1,29 +0,0 @@
1
- import assert from 'minimalistic-assert'
2
-
3
- import { getExtraFeeData } from './get-fee.js'
4
-
5
- const getFeeAsyncFactory = ({ assetClientInterface, createTx }) => {
6
- assert(assetClientInterface, 'assetClientInterface is required')
7
- assert(createTx, 'createTx is required')
8
-
9
- return async (params) => {
10
- const { asset } = params
11
- const { unsignedTx, gasPrice, tipGasPrice, gasLimit } = params.unsignedTx
12
- ? params
13
- : await createTx(params)
14
- const fee = asset.feeAsset.currency.parse(unsignedTx.txMeta.fee)
15
- const coinAmount = asset.currency.parse(unsignedTx.txMeta.amount)
16
- const extraFeeData = getExtraFeeData({ asset, amount: coinAmount })
17
- return {
18
- fee,
19
- extraFeeData,
20
- unsignedTx,
21
- // deprecated, exchanges uses these params to recreate the tx during send. It should just use unsignedTx...
22
- gasPrice,
23
- tipGasPrice,
24
- gasLimit,
25
- }
26
- }
27
- }
28
-
29
- export default getFeeAsyncFactory