@exodus/ethereum-api 10.0.0 → 10.1.1

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,24 @@
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
+ ## [10.1.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@10.1.0...@exodus/ethereum-api@10.1.1) (2026-10-09)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * migrate to @exodus/basic-utils 5 ([#8894](https://github.com/ExodusMovement/assets/issues/8894)) ([ed125f3](https://github.com/ExodusMovement/assets/commit/ed125f3257785c92a3b8f8239a2e9eefe5dc6772))
12
+
13
+
14
+
15
+ ## [10.1.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@10.0.0...@exodus/ethereum-api@10.1.0) (2026-10-05)
16
+
17
+
18
+ ### Features
19
+
20
+ * **ethereum:** add evm staking api core with Everstake ETH and polygon adapters ([#8775](https://github.com/ExodusMovement/assets/issues/8775)) ([2d90106](https://github.com/ExodusMovement/assets/commit/2d901069989be82eb56aa253d5171e4f6af97356))
21
+
22
+
23
+
6
24
  ## [10.0.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@9.0.3...@exodus/ethereum-api@10.0.0) (2026-10-05)
7
25
 
8
26
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "10.0.0",
3
+ "version": "10.1.1",
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",
@@ -23,7 +23,7 @@
23
23
  "@exodus/asset": "^2.0.4",
24
24
  "@exodus/asset-lib": "^5.9.2",
25
25
  "@exodus/assets": "^11.4.0",
26
- "@exodus/basic-utils": "^3.0.1",
26
+ "@exodus/basic-utils": "^5.1.0",
27
27
  "@exodus/bip44-constants": "^195.0.0",
28
28
  "@exodus/crypto": "^1.0.0-rc.26",
29
29
  "@exodus/currency": "^6.0.1",
@@ -66,5 +66,5 @@
66
66
  "type": "git",
67
67
  "url": "git+https://github.com/ExodusMovement/assets.git"
68
68
  },
69
- "gitHead": "83a0e678debb92fbfcefd92e7bd0453d1a92136b"
69
+ "gitHead": "41e6c1bb6a3d919f08cc3ee38068159259b335be"
70
70
  }
@@ -176,7 +176,7 @@ export const createTransactionPrivacyResult = ({
176
176
  return { nonce, txId }
177
177
  }
178
178
 
179
- const sendPrivateBundle = async ({ unsignedTxBundle }) => {
179
+ const sendPrivateBundle = async ({ unsignedTxBundle, onBeforeSign }) => {
180
180
  assert(
181
181
  Array.isArray(unsignedTxBundle) && unsignedTxBundle.length > 0,
182
182
  'expected non-empty unsignedTxBundle'
@@ -188,11 +188,11 @@ export const createTransactionPrivacyResult = ({
188
188
  await assertNonZeroTipGasPrice({ asset, assetClientInterface, unsignedTx })
189
189
  }
190
190
 
191
- const signTxResults = await Promise.all(
192
- unsignedTxBundle.map(({ asset, unsignedTx, walletAccount }) =>
193
- signTx({ assetClientInterface, asset, unsignedTx, walletAccount })
194
- )
195
- )
191
+ const signTxResults = []
192
+ for (const [index, { asset, unsignedTx, walletAccount }] of unsignedTxBundle.entries()) {
193
+ onBeforeSign?.({ index, total: unsignedTxBundle.length })
194
+ signTxResults.push(await signTx({ assetClientInterface, asset, unsignedTx, walletAccount }))
195
+ }
196
196
 
197
197
  const maybeBundleResult = await broadcastPrivateBundle({
198
198
  txs: signTxResults.map(({ rawTx }) => rawTx.toString('hex')),
@@ -49,6 +49,11 @@ import { estimateL1DataFeeFactory, getL1GetFeeFactory } from './optimism-gas/ind
49
49
  import { createSendValidations } from './send-validations.js'
50
50
  import { serverBasedFeeMonitorFactoryFactory } from './server-based-fee-monitor.js'
51
51
  import { stakingApiFactory } from './staking/api/index.js'
52
+ import {
53
+ applyStakingApiOverrides,
54
+ composeStakingApi,
55
+ isStakingApiEnabled,
56
+ } from './staking/evm/compose-staking-api.js'
52
57
  import { createTxFactory } from './tx-create.js'
53
58
  import { txSendFactory } from './tx-send/index.js'
54
59
  import { createWeb3API } from './web3/index.js'
@@ -135,8 +140,14 @@ export const createAssetFactory = ({
135
140
  supportsTransactionPrivacy,
136
141
  transactionAssessment,
137
142
  multicall3Address,
143
+ stakingApiEnabled: stakingApiOverrides,
138
144
  } = configWithOverrides
139
145
 
146
+ const assetStakingDependencies = applyStakingApiOverrides({
147
+ stakingDependencies,
148
+ overrides: stakingApiOverrides,
149
+ })
150
+
140
151
  const asset = assets[base.name]
141
152
 
142
153
  const { monitorType, serverUrl, monitorInterval } = resolveMonitorSettings({
@@ -206,7 +217,7 @@ export const createAssetFactory = ({
206
217
  assetClientInterface,
207
218
  server,
208
219
  stakingConfiguration,
209
- stakingDependencies,
220
+ stakingDependencies: assetStakingDependencies,
210
221
  createRpcRequestAccumulator,
211
222
  },
212
223
  assets
@@ -236,6 +247,23 @@ export const createAssetFactory = ({
236
247
  supportsTransactionPrivacy,
237
248
  })
238
249
 
250
+ const stakingApiEnabled =
251
+ supportsStaking && isStakingApiEnabled(assetStakingDependencies[asset.name])
252
+
253
+ const createAssetStakingApi = () =>
254
+ composeStakingApi({
255
+ assetClientInterface,
256
+ stakingDependencies: assetStakingDependencies[asset.name],
257
+ legacyStakingApi: stakingApiFactory({
258
+ assetName: asset.name,
259
+ currency: asset.currency,
260
+ assetClientInterface,
261
+ server,
262
+ stakingConfiguration: stakingConfiguration[asset.name],
263
+ stakingDependencies: assetStakingDependencies[asset.name],
264
+ }),
265
+ })
266
+
239
267
  const features = {
240
268
  accountState: true,
241
269
  customTokens,
@@ -254,6 +282,7 @@ export const createAssetFactory = ({
254
282
  supportsCustomFees,
255
283
  supportsTransactionPrivacy: Boolean(supportsTransactionPrivacy),
256
284
  ...(supportsStaking && { staking: {} }),
285
+ ...(stakingApiEnabled && { stakingApi: true }),
257
286
  ...(delisted && { delisted }),
258
287
  }
259
288
 
@@ -393,15 +422,8 @@ export const createAssetFactory = ({
393
422
  signer ? signMessageWithSigner({ message, signer }) : signMessage({ privateKey, message }),
394
423
  ...(checkTx && { checkTx }),
395
424
  ...(supportsStaking &&
396
- stakingDependencies[asset.name] && {
397
- staking: stakingApiFactory({
398
- assetName: asset.name,
399
- currency: asset.currency,
400
- assetClientInterface,
401
- server,
402
- stakingConfiguration: stakingConfiguration[asset.name],
403
- stakingDependencies: stakingDependencies[asset.name],
404
- }),
425
+ assetStakingDependencies[asset.name] && {
426
+ staking: createAssetStakingApi(),
405
427
  }),
406
428
  validateAssetId: address.validate,
407
429
  web3,
@@ -3,6 +3,7 @@ import { createContract } from '@exodus/ethereum-lib'
3
3
  import assert from 'minimalistic-assert'
4
4
 
5
5
  import { stakingApiFactory } from './staking/api/index.js'
6
+ import { composeStakingApi, isStakingApiEnabled } from './staking/evm/compose-staking-api.js'
6
7
  import {
7
8
  isPolygonClaimUndelegate,
8
9
  isPolygonDelegate,
@@ -48,7 +49,13 @@ const getCreateBaseToken =
48
49
  }) =>
49
50
  ({ name, contract, features, ...tokenDef }) => {
50
51
  assert(getBalances, 'getBalances is required')
51
- const tokenSpecificFeatures = name === 'polygon' ? { staking: {} } : {}
52
+ const tokenSpecificFeatures =
53
+ name === 'polygon'
54
+ ? {
55
+ staking: {},
56
+ ...(isStakingApiEnabled(stakingDependencies[name]) && { stakingApi: true }),
57
+ }
58
+ : {}
52
59
 
53
60
  const tokenFeatures = { ...defaultTokenFeatures, ...features, ...tokenSpecificFeatures }
54
61
  const baseApi = {
@@ -72,14 +79,18 @@ const getCreateBaseToken =
72
79
  }
73
80
 
74
81
  if (name === 'polygon' && stakingDependencies[name]) {
75
- baseApi.staking = stakingApiFactory({
76
- assetName: tokenAsset.name,
77
- currency: tokenAsset.currency,
82
+ baseApi.staking = composeStakingApi({
78
83
  assetClientInterface,
79
- server,
80
- stakingConfiguration: stakingConfiguration[name],
81
84
  stakingDependencies: stakingDependencies[name],
82
- createRpcRequestAccumulator,
85
+ legacyStakingApi: stakingApiFactory({
86
+ assetName: tokenAsset.name,
87
+ currency: tokenAsset.currency,
88
+ assetClientInterface,
89
+ server,
90
+ stakingConfiguration: stakingConfiguration[name],
91
+ stakingDependencies: stakingDependencies[name],
92
+ createRpcRequestAccumulator,
93
+ }),
83
94
  })
84
95
  }
85
96
 
package/src/index.js CHANGED
@@ -43,6 +43,20 @@ export { createEncodeMultisigContract } from './multisig-address.js'
43
43
  export { FantomStaking, stakingProviderClientFactory } from './staking/index.js'
44
44
  export { EthLikeRpcRequestAccumulator } from './multicall3/index.js'
45
45
  export { createCachedStakingRead } from './staking/cached-read.js'
46
+ export { createStakingTxs, sendStakingTxs, STAKING_TX_MODE } from './staking/evm/staking-txs.js'
47
+ export { createEvmStakingApi } from './staking/evm/create-evm-staking-api.js'
48
+ export {
49
+ createRejectionError as createStakingRejectionError,
50
+ createSubmitError as createStakingSubmitError,
51
+ fail as failStakingCheck,
52
+ pass as passStakingCheck,
53
+ sumOperationFees as sumStakingOperationFees,
54
+ warnIfBelow as warnIfStakingBalanceBelow,
55
+ } from './staking/evm/staking-errors.js'
56
+ export {
57
+ createEverstakeStakingAdapter,
58
+ ETH_STAKING_GAS_LIMITS,
59
+ } from './staking/ethereum/staking-adapter.js'
46
60
  export { ethereumStakingDeps } from './staking/ethereum/deps.js'
47
61
  export { getEthereumStakingInfo, createEthereumStakingService } from './staking/ethereum/service.js'
48
62
  export { EthereumStaking } from './staking/ethereum/api.js'
@@ -65,6 +79,7 @@ export {
65
79
  isPolygonReward,
66
80
  isPolygonClaimUndelegate,
67
81
  isPendingPolygonUndelegateTxInEthereumTxLog,
82
+ isPolygonUndelegateTxInEthereumTxLog,
68
83
  } from './staking/matic/matic-staking-utils.js'
69
84
 
70
85
  export { fetchTxPreview } from './simulate-tx/fetch-tx-preview.js'
@@ -36,8 +36,9 @@ export const stakingApiFactory = ({
36
36
 
37
37
  return {
38
38
  ...stakingService,
39
+ // @deprecated legacy staking api, remove once evm staking unification is done
39
40
  isStaking: async ({ isDelegating }) => isDelegating,
40
41
  isUnstaking: async ({ isUndelegateInProgress }) => isUndelegateInProgress,
41
- isUnstaked: async ({ canClaimUndelegateBalance }) => canClaimUndelegateBalance,
42
+ isUnstaked: async ({ canClaimUndelegatedBalance }) => canClaimUndelegatedBalance,
42
43
  }
43
44
  }
@@ -7,9 +7,8 @@ import assert from 'minimalistic-assert'
7
7
  import { EVM_ERROR_REASONS, withErrorReason } from '../../error-wrapper.js'
8
8
  import { EthLikeRpcRequestAccumulator } from '../../multicall3/index.js'
9
9
  import { createCachedStakingRead } from '../cached-read.js'
10
+ import { ETH_STAKING_MIN_AMOUNT } from './min-amount.js'
10
11
 
11
- // TODO: Shouldn't this be a function of `ethereumStakingState.minDelegateAmount`?
12
- const MIN_AMOUNT = '0.1'
13
12
  const RETRY_DELAYS = ['10s']
14
13
 
15
14
  const EVERSTAKE_API_URL = 'https://eth-clarity.a.exodus.io/everstake-rewards'
@@ -46,7 +45,7 @@ export class EthereumStaking {
46
45
  return txInputHex.toLowerCase().startsWith(EthereumStaking.METHODS_IDS.DELEGATE)
47
46
  }
48
47
 
49
- constructor(assetName, currency, minAmount = MIN_AMOUNT, server) {
48
+ constructor(assetName, currency, minAmount = ETH_STAKING_MIN_AMOUNT, server) {
50
49
  this.asset = { name: assetName, currency }
51
50
  const accountingAddress =
52
51
  EthereumStaking.addresses[assetName].EVERSTAKE_ADDRESS_CONTRACT_ACCOUNTING
@@ -1,5 +1,6 @@
1
1
  import { EthereumStaking } from './api.js'
2
2
  import { createEthereumStakingService, getEthereumStakingInfo } from './service.js'
3
+ import { createEverstakeStakingAdapter } from './staking-adapter.js'
3
4
 
4
5
  /** Staking server factory for ethereum (used by stakingApiFactory). */
5
6
  export const stakingServerFactory = ({ assetName, currency, server, minAmount }) =>
@@ -24,6 +25,7 @@ export const stakingServiceFactory = ({ assetClientInterface, server, stakingSer
24
25
  useQueueCache,
25
26
  background,
26
27
  }),
28
+ // @deprecated legacy staking api, remove once evm staking unification is done
27
29
  stake: service.delegate,
28
30
  unstake: service.undelegate,
29
31
  claimUnstaked: service.claimUndelegatedBalance,
@@ -34,4 +36,5 @@ export const stakingServiceFactory = ({ assetClientInterface, server, stakingSer
34
36
  export const ethereumStakingDeps = {
35
37
  stakingServerFactory,
36
38
  stakingServiceFactory,
39
+ createStakingAdapter: ({ service }) => createEverstakeStakingAdapter({ service }),
37
40
  }
@@ -0,0 +1 @@
1
+ export const ETH_STAKING_MIN_AMOUNT = '0.1'