@exodus/ethereum-api 8.54.1 → 8.56.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,32 @@
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.56.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.55.0...@exodus/ethereum-api@8.56.0) (2025-11-04)
7
+
8
+
9
+ ### Features
10
+
11
+
12
+ * feat: monad-testnet (#6706)
13
+
14
+
15
+
16
+ ## [8.55.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.54.1...@exodus/ethereum-api@8.55.0) (2025-11-03)
17
+
18
+
19
+ ### Features
20
+
21
+
22
+ * feat: bump @exodus/safe-string@1.2.1 (#6775)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+
28
+ * fix: enable `update-nonce` to work for `unsignedTx`s that lack a `transactionBuffer` (#6832)
29
+
30
+
31
+
6
32
  ## [8.54.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.54.0...@exodus/ethereum-api@8.54.1) (2025-10-24)
7
33
 
8
34
  **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.54.1",
3
+ "version": "8.56.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",
@@ -29,13 +29,13 @@
29
29
  "@exodus/bip44-constants": "^195.0.0",
30
30
  "@exodus/crypto": "^1.0.0-rc.26",
31
31
  "@exodus/currency": "^6.0.1",
32
- "@exodus/ethereum-lib": "^5.18.3",
32
+ "@exodus/ethereum-lib": "^5.18.5",
33
33
  "@exodus/ethereum-meta": "^2.9.1",
34
34
  "@exodus/ethereumholesky-meta": "^2.0.5",
35
35
  "@exodus/ethereumjs": "^1.8.0",
36
36
  "@exodus/fetch": "^1.3.0",
37
37
  "@exodus/models": "^12.13.0",
38
- "@exodus/safe-string": "^1.2.0",
38
+ "@exodus/safe-string": "^1.2.1",
39
39
  "@exodus/simple-retry": "^0.0.6",
40
40
  "@exodus/solidity-contract": "^1.3.0",
41
41
  "@exodus/web3-ethereum-utils": "^4.5.1",
@@ -67,5 +67,5 @@
67
67
  "type": "git",
68
68
  "url": "git+https://github.com/ExodusMovement/assets.git"
69
69
  },
70
- "gitHead": "9d2c245deb27cf3ce56eef0d7ad5c0ab8bd4bc02"
70
+ "gitHead": "7d384b740c90be9f6c39a4dcfe33508b27b5ea7a"
71
71
  }
@@ -1,3 +1,6 @@
1
+ import { memoize } from '@exodus/basic-utils'
2
+ import assert from 'minimalistic-assert'
3
+
1
4
  import { estimateGasLimit, scaleGasLimitEstimate } from '../../gas-estimation.js'
2
5
  import { createWatchTx as defaultCreateWatch } from '../../watch-tx.js'
3
6
  import { stakingProviderClientFactory } from '../staking-provider-client.js'
@@ -20,20 +23,34 @@ const MINIMUM_DELEGATION_GAS_LIMIT = 180_000
20
23
 
21
24
  const EXTRA_GAS_LIMIT = 20_000 // extra gas Limit to prevent tx failing if something change on pool state (till tx is in mempool)
22
25
 
26
+ const getStakingApi = memoize(
27
+ (asset) => new EthereumStaking(asset, undefined, asset.server),
28
+ (asset) => asset.name
29
+ )
30
+
23
31
  export function createEthereumStakingService({
24
- asset,
32
+ asset: deprectedArg, // @deprecated use `assetName` instead
33
+ assetName,
25
34
  assetClientInterface,
26
35
  createWatchTx = defaultCreateWatch,
27
36
  stakingProvider = stakingProviderClientFactory(),
28
37
  }) {
29
- const staking = new EthereumStaking(asset, undefined, asset.server)
30
- const minAmount = staking.minAmount
38
+ if (deprectedArg && !assetName) {
39
+ console.log('using deprecated ARG `asset` instead of `assetName`')
40
+ assetName = deprectedArg.name
41
+ }
42
+
43
+ const getAsset = memoize(async (baseAssetName) => {
44
+ const { [assetName]: asset } = await assetClientInterface.getAssetsForNetwork({ baseAssetName })
45
+ return asset
46
+ })
31
47
 
32
48
  const getTransactionProps = async ({ feeData, walletAccount }) => {
49
+ const asset = await getAsset(assetName)
33
50
  let address
34
51
  ;[address, feeData] = await Promise.all([
35
52
  assetClientInterface.getReceiveAddress({
36
- assetName: asset.name,
53
+ assetName,
37
54
  walletAccount,
38
55
  }),
39
56
  resolveFeeData({ asset, assetClientInterface, feeData }),
@@ -44,6 +61,8 @@ export function createEthereumStakingService({
44
61
  }
45
62
 
46
63
  async function delegate({ walletAccount, amount, feeData } = Object.create(null)) {
64
+ const asset = await getAsset(assetName)
65
+ const staking = getStakingApi(asset)
47
66
  amount = amountToCurrency({ asset, amount })
48
67
 
49
68
  let delegatorAddress
@@ -78,10 +97,10 @@ export function createEthereumStakingService({
78
97
  })
79
98
 
80
99
  // Goerli is not supported
81
- if (asset.name === 'ethereum') {
100
+ if (assetName === 'ethereum') {
82
101
  await stakingProvider.notifyStaking({
83
102
  txId,
84
- asset: asset.name,
103
+ asset: assetName,
85
104
  delegator: delegatorAddress,
86
105
  amount: amount.toBaseString(),
87
106
  })
@@ -97,6 +116,8 @@ export function createEthereumStakingService({
97
116
  minAmount,
98
117
  feeData,
99
118
  }) {
119
+ const asset = await getAsset(assetName)
120
+ const staking = getStakingApi(asset)
100
121
  const leftOver = pendingAmount.sub(resquestedAmount)
101
122
 
102
123
  if (leftOver.isPositive && leftOver.lt(minAmount)) {
@@ -126,6 +147,8 @@ export function createEthereumStakingService({
126
147
  }
127
148
 
128
149
  async function getUndelegateData({ delegatorAddress, resquestedAmount, pendingAmount, feeData }) {
150
+ const asset = await getAsset(assetName)
151
+ const staking = getStakingApi(asset)
129
152
  const canUnstake = resquestedAmount.gt(pendingAmount)
130
153
 
131
154
  if (!canUnstake) {
@@ -159,6 +182,10 @@ export function createEthereumStakingService({
159
182
  * @returns total undelegete fee
160
183
  */
161
184
  async function estimateUndelegate({ walletAccount, amount: resquestedAmount, feeData }) {
185
+ const asset = await getAsset(assetName)
186
+ const staking = getStakingApi(asset)
187
+ const minAmount = getMinAmount(asset)
188
+
162
189
  let delegatorAddress
163
190
  ;({ feeData, delegatorAddress } = await getTransactionProps({ feeData, walletAccount }))
164
191
 
@@ -197,6 +224,10 @@ export function createEthereumStakingService({
197
224
  async function undelegate(
198
225
  { walletAccount, amount, feeData, waitForConfirmation = true } = Object.create(null)
199
226
  ) {
227
+ const asset = await getAsset(assetName)
228
+ const staking = getStakingApi(asset)
229
+ const minAmount = getMinAmount(asset)
230
+
200
231
  /*
201
232
  unstakePending balance (not yet in validator) + unstake balance (in validator)
202
233
  1. give priority to unstakePending (based on the amount)
@@ -238,7 +269,7 @@ export function createEthereumStakingService({
238
269
  //
239
270
  // NOTE: This will invalidate previous transaction fee estimates!
240
271
  if (waitForConfirmation) {
241
- feeData = await assetClientInterface.getFeeData({ assetName: asset.name })
272
+ feeData = await assetClientInterface.getFeeData({ assetName })
242
273
  }
243
274
  }
244
275
 
@@ -260,10 +291,10 @@ export function createEthereumStakingService({
260
291
  }
261
292
 
262
293
  // Testnet assets do not support delegations tracking
263
- if (txId && asset.name === 'ethereum') {
294
+ if (txId && assetName === 'ethereum') {
264
295
  await stakingProvider.notifyUnstaking({
265
296
  txId,
266
- asset: asset.name,
297
+ asset: assetName,
267
298
  delegator: delegatorAddress,
268
299
  amount: resquestedAmount.toBaseString(),
269
300
  })
@@ -273,6 +304,9 @@ export function createEthereumStakingService({
273
304
  }
274
305
 
275
306
  async function claimUndelegatedBalance({ walletAccount, feeData } = Object.create(null)) {
307
+ const asset = await getAsset(assetName)
308
+ const staking = getStakingApi(asset)
309
+
276
310
  let delegatorAddress
277
311
  ;({ feeData, delegatorAddress } = await getTransactionProps({ feeData, walletAccount }))
278
312
 
@@ -304,6 +338,9 @@ export function createEthereumStakingService({
304
338
  }
305
339
 
306
340
  async function estimateDelegateOperation({ walletAccount, operation, args, feeData }) {
341
+ const asset = await getAsset(assetName)
342
+ const staking = getStakingApi(asset)
343
+
307
344
  const requestedAmount = args.amount
308
345
  ? amountToCurrency({ asset, amount: args.amount })
309
346
  : asset.currency.ZERO
@@ -360,6 +397,8 @@ export function createEthereumStakingService({
360
397
  }
361
398
 
362
399
  async function estimateTxFee({ from, to, amount, txInput, feeData }) {
400
+ const asset = await getAsset(assetName)
401
+
363
402
  amount = amount || asset.currency.ZERO
364
403
  from = from.toLowerCase()
365
404
 
@@ -395,18 +434,17 @@ export function createEthereumStakingService({
395
434
  }
396
435
 
397
436
  /** Returns the minimum possible amount that can be staked. */
398
- function getMinAmount() {
399
- return staking.minAmount
437
+ function getMinAmount(asset) {
438
+ assert(asset, 'getMinAmount: `asset` is required')
439
+ return getStakingApi(asset).minAmount
400
440
  }
401
441
 
402
442
  /** Determine the maximum possible stake for a given spendable amount. */
403
- async function getDelegateSelectAllAmount({
404
- walletAccount,
405
- spendableForStaking = asset.currency.ZERO,
406
- feeData,
407
- }) {
408
- const minAmount = getMinAmount()
443
+ async function getDelegateSelectAllAmount({ walletAccount, spendableForStaking, feeData }) {
444
+ const asset = await getAsset(assetName)
445
+ const minAmount = getMinAmount(asset)
409
446
 
447
+ if (!spendableForStaking) spendableForStaking = asset.currency.ZERO
410
448
  ;({ feeData } = await getTransactionProps({ feeData, walletAccount }))
411
449
 
412
450
  // If the caller hasn't specified a value of `spendableForStaking`
@@ -37,6 +37,10 @@ const txSendFactory = ({ assetClientInterface, createTx }) => {
37
37
  })
38
38
  }
39
39
 
40
+ // NOTE: An `unsignedTx.txData.transactionBuffer` may be optional
41
+ // in `unsignedTx`, since a `providedUnsignedTx` may be
42
+ // truthy but composed only of `legacyParams`:
43
+ // https://github.com/ExodusMovement/assets/blob/4e3e873e3f5bfa8fce36f60be7d95a3dba9546e4/ethereum/ethereum-lib/src/unsigned-tx/parse-unsigned-tx.js#L104C5-L104C6
40
44
  const { unsignedTx } = await resolveUnsignedTx()
41
45
 
42
46
  const parsedTx = parseUnsignedTx({ asset, unsignedTx })
@@ -96,10 +100,11 @@ const txSendFactory = ({ assetClientInterface, createTx }) => {
96
100
  forceFromNode: true,
97
101
  })
98
102
 
103
+ // TODO: should return an unsignedTx -> i.e. completely replicate the transaction
99
104
  unsignedTx.txData.transactionBuffer = updateNonce({
100
105
  chainId: baseAsset.chainId,
101
- transactionBuffer: unsignedTx.txData.transactionBuffer,
102
106
  newNonce,
107
+ unsignedTx,
103
108
  })
104
109
  ;({ txId, rawTx } = await signTx({ asset, unsignedTx, walletAccount }))
105
110