@exodus/ethereum-api 8.33.7 → 8.34.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,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.34.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.33.7...@exodus/ethereum-api@8.34.0) (2025-04-08)
7
+
8
+
9
+ ### Features
10
+
11
+
12
+ * feat: demote batch of 20 tokens (#5347)
13
+
14
+
15
+
6
16
  ## [8.33.7](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.33.6...@exodus/ethereum-api@8.33.7) (2025-04-03)
7
17
 
8
18
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "8.33.7",
3
+ "version": "8.34.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",
@@ -28,8 +28,8 @@
28
28
  "@exodus/bip44-constants": "^195.0.0",
29
29
  "@exodus/crypto": "^1.0.0-rc.13",
30
30
  "@exodus/currency": "^6.0.1",
31
- "@exodus/ethereum-lib": "^5.9.0",
32
- "@exodus/ethereum-meta": "^2.3.0",
31
+ "@exodus/ethereum-lib": "^5.10.0",
32
+ "@exodus/ethereum-meta": "^2.5.0",
33
33
  "@exodus/ethereumholesky-meta": "^2.0.2",
34
34
  "@exodus/ethereumjs": "^1.0.0",
35
35
  "@exodus/fetch": "^1.3.0",
@@ -64,5 +64,5 @@
64
64
  "type": "git",
65
65
  "url": "git+https://github.com/ExodusMovement/assets.git"
66
66
  },
67
- "gitHead": "c181bb6d727d45fe17b31d1cf1b70eeb34fb7fd4"
67
+ "gitHead": "1684d29840942a8b5dce8dc54e3de02d86964b36"
68
68
  }
@@ -100,8 +100,8 @@ export function sendRawTransaction(asset) {
100
100
  export async function transactionExists({ asset, txId }) {
101
101
  const server = getServer(asset)
102
102
  txId = normalizeTxId(txId)
103
- const txResult = server.getTransactionByHash(txId)
104
- return txResult && txResult.hash === txId
103
+ const txResult = await server.getTransactionByHash(txId)
104
+ return Boolean(txResult && txResult.hash === txId)
105
105
  }
106
106
 
107
107
  export async function getTransaction({ asset, txId }) {
@@ -9,6 +9,58 @@ import { getNftArguments } from '../nft-utils.js'
9
9
  import getFeeInfo from './get-fee-info.js'
10
10
  import { resolveNonce } from './nonce-utils.js'
11
11
 
12
+ // Exodus enforces a strict invariant that `sendAll` transactions
13
+ // must not leave any dust in the sender's account. Currently, the
14
+ // assets library has the expectation that the client frontend
15
+ // should calculate the precise amount to send, but due to the
16
+ // sheer amount of variables involved when resolving a `gasPrice`,
17
+ // this is a significant undertaking.
18
+ //
19
+ // Therefore, although clients try their very best to calculate
20
+ // the correct amount, in cases this fails we can fall back to
21
+ // the implementation defined here with a warning.
22
+ // eslint-disable-next-line camelcase
23
+ export const HACK_maybeRefineSendAllAmount = async ({
24
+ amount: providedAmount,
25
+ asset,
26
+ assetClientInterface,
27
+ walletAccount,
28
+ gasLimit,
29
+ gasPrice,
30
+ }) => {
31
+ try {
32
+ const { name: assetName } = asset
33
+
34
+ const [txLog, accountState] = await Promise.all([
35
+ assetClientInterface.getTxLog({
36
+ assetName,
37
+ walletAccount,
38
+ }),
39
+ assetClientInterface.getAccountState({
40
+ assetName,
41
+ walletAccount,
42
+ }),
43
+ ])
44
+
45
+ const { spendable } = await asset.api.getBalances({ asset, txLog, accountState })
46
+ const maxGasCost = gasPrice.mul(gasLimit)
47
+
48
+ if (maxGasCost.gt(spendable)) throw new Error('transaction gas cost exceeds spendable balance')
49
+
50
+ const expectedSendAllAmount = spendable.sub(maxGasCost)
51
+
52
+ // If the client attempted to send the correct
53
+ // amount, good job! You get a cookie!
54
+ if (providedAmount.equals(expectedSendAllAmount)) return null
55
+
56
+ // The client attempted to `sendAll` using the incorrect amount.
57
+ return expectedSendAllAmount
58
+ } catch (e) {
59
+ console.error('failed to refine send all amount', e)
60
+ return null
61
+ }
62
+ }
63
+
12
64
  const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
13
65
  assert(assetClientInterface, 'assetClientInterface is required')
14
66
  assert(createUnsignedTx, 'createUnsignedTx is required')
@@ -298,6 +350,8 @@ const createTx = async ({
298
350
  feeOpts,
299
351
  })
300
352
 
353
+ const isSendAllBaseAsset = isSendAll && !isToken
354
+
301
355
  if (eip1559Enabled) {
302
356
  if (customGasPrice) {
303
357
  gasPrice = customGasPrice // aka maxFeePerGas
@@ -310,12 +364,31 @@ const createTx = async ({
310
364
  tipGasPrice = customGasPrice // aka maxPriorityFeePerGas
311
365
  }
312
366
 
313
- if (isSendAll && !isToken) {
367
+ if (isSendAllBaseAsset) {
314
368
  // force consuming all gas
315
369
  tipGasPrice = gasPrice
316
370
  }
371
+ }
317
372
 
318
- // gasLimit = customGasLimit
373
+ // HACK: If we are handling a send all transaction, we must ensure
374
+ // the send all invariant is maintained before producing the
375
+ // final transaction.
376
+ const maybeOverrideSendAllAmount =
377
+ isSendAllBaseAsset &&
378
+ (await HACK_maybeRefineSendAllAmount({
379
+ amount,
380
+ asset,
381
+ assetClientInterface,
382
+ walletAccount,
383
+ gasLimit,
384
+ gasPrice,
385
+ }))
386
+
387
+ if (maybeOverrideSendAllAmount) {
388
+ console.log(
389
+ `Attempted to execute a sendAll transaction with an amount of ${amount.toDefaultString({ unit: true })}, but this would fail to maintain the no dust invariant! Overriding with ${maybeOverrideSendAllAmount.toDefaultString({ unit: true })}.`
390
+ )
391
+ amount = maybeOverrideSendAllAmount
319
392
  }
320
393
 
321
394
  const unsignedTx = await createUnsignedTx({