@exodus/ethereum-api 8.47.1 → 8.49.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.49.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.48.0...@exodus/ethereum-api@8.49.0) (2025-09-16)
7
+
8
+
9
+ ### Features
10
+
11
+
12
+ * feat: improve RPC error visibility (#6067)
13
+
14
+
15
+
16
+ ## [8.48.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.47.1...@exodus/ethereum-api@8.48.0) (2025-09-10)
17
+
18
+
19
+ ### Features
20
+
21
+
22
+ * feat(ethereum): add displayAddess to asset.address (#6363)
23
+
24
+
25
+
6
26
  ## [8.47.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.47.0...@exodus/ethereum-api@8.47.1) (2025-09-08)
7
27
 
8
28
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "8.47.1",
3
+ "version": "8.49.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",
@@ -34,6 +34,7 @@
34
34
  "@exodus/ethereumjs": "^1.0.0",
35
35
  "@exodus/fetch": "^1.3.0",
36
36
  "@exodus/models": "^12.13.0",
37
+ "@exodus/safe-string": "^1.2.0",
37
38
  "@exodus/simple-retry": "^0.0.6",
38
39
  "@exodus/solidity-contract": "^1.1.3",
39
40
  "@exodus/web3-ethereum-utils": "^4.2.1",
@@ -52,6 +53,7 @@
52
53
  "devDependencies": {
53
54
  "@exodus/assets-testing": "^1.0.0",
54
55
  "@exodus/bsc-meta": "^2.5.1",
56
+ "@exodus/errors": "^3.3.0",
55
57
  "@exodus/ethereumarbone-meta": "^2.1.2",
56
58
  "@exodus/fantommainnet-meta": "^2.0.5",
57
59
  "@exodus/matic-meta": "^2.2.7",
@@ -64,5 +66,5 @@
64
66
  "type": "git",
65
67
  "url": "git+https://github.com/ExodusMovement/assets.git"
66
68
  },
67
- "gitHead": "fbda86007124112138bfe661e1b25655e6107762"
69
+ "gitHead": "a3e06b6b731dd294513b514786db037f874ced76"
68
70
  }
@@ -16,6 +16,7 @@ import {
16
16
  signUnsignedTxWithSigner,
17
17
  validateFactory,
18
18
  } from '@exodus/ethereum-lib'
19
+ import { toChecksumAddress } from '@exodus/ethereumjs/util'
19
20
  import lodash from 'lodash'
20
21
  import assert from 'minimalistic-assert'
21
22
 
@@ -131,6 +132,7 @@ export const createAssetFactory = ({
131
132
  validate: validateFactory({ chainId, useEip1191ChainIdChecksum }),
132
133
  hasChecksum,
133
134
  isContract: server.isContract,
135
+ displayAddress: (addr) => toChecksumAddress(addr),
134
136
  }
135
137
 
136
138
  const bip44 = customBip44 || bip44Constants['ETH']
@@ -1,9 +1,11 @@
1
1
  import { bufferToHex } from '@exodus/ethereumjs/util'
2
+ import { safeString } from '@exodus/safe-string'
2
3
  import SolidityContract from '@exodus/solidity-contract'
3
4
  import EventEmitter from 'events/events.js'
4
5
  import lodash from 'lodash'
5
6
 
6
7
  import { fromHexToString } from '../number-utils.js'
8
+ import { errorMessageToSafeHint } from './errors.js'
7
9
 
8
10
  const { isEmpty } = lodash
9
11
 
@@ -52,8 +54,11 @@ export default class ApiCoinNodesServer extends EventEmitter {
52
54
  const result = response?.result
53
55
  const error = response?.error
54
56
  if (error || result === undefined) {
55
- const message = error?.message || error?.code || 'no result'
56
- throw new Error(`Bad rpc response: ${message}`)
57
+ const message = error?.message || error?.code || safeString`no result`
58
+
59
+ const revisedError = new Error(`Bad rpc response: ${message}`)
60
+ revisedError.hint = safeString`Bad rpc response: ${errorMessageToSafeHint(message)}`
61
+ throw revisedError
57
62
  }
58
63
 
59
64
  return result
@@ -66,9 +66,6 @@ const fetchHttpRequest = ({ baseApiPath, path, method, body }) => {
66
66
  return fetchJson(url, fetchOptions)
67
67
  }
68
68
 
69
- const fetchRpcHttpRequest = ({ baseApiPath, body }) =>
70
- fetchHttpRequest({ baseApiPath, path: '/rpc', method: 'POST', body })
71
-
72
69
  async function fetchJsonRetry(url, fetchOptions) {
73
70
  const waitTimes = ['3s']
74
71
  const fetchWithRetry = retry(fetchJson, { delayTimesMs: waitTimes })
@@ -187,6 +184,9 @@ export default class ClarityServerV2 extends ClarityServer {
187
184
  }
188
185
  }
189
186
 
187
+ fetchRpcHttpRequest = ({ baseApiPath, body }) =>
188
+ fetchHttpRequest({ baseApiPath, path: '/rpc', method: 'POST', body })
189
+
190
190
  async sendRpcRequest(rpcRequest) {
191
191
  try {
192
192
  return await super.sendRpcRequest(rpcRequest)
@@ -196,20 +196,24 @@ export default class ClarityServerV2 extends ClarityServer {
196
196
  if (err.message !== RPC_REQUEST_TIMEOUT) throw err
197
197
 
198
198
  const { baseApiPath } = this
199
- return fetchRpcHttpRequest({ baseApiPath, body: rpcRequest })
199
+ return this.fetchRpcHttpRequest({ baseApiPath, body: rpcRequest })
200
200
  }
201
201
  }
202
202
 
203
203
  async sendRawTransaction(...params) {
204
204
  const { baseApiPath } = this
205
205
  const request = this.sendRawTransactionRequest(...params)
206
- return this.handleJsonRPCResponse(await fetchRpcHttpRequest({ baseApiPath, body: request }))
206
+ return this.handleJsonRPCResponse(
207
+ await this.fetchRpcHttpRequest({ baseApiPath, body: request })
208
+ )
207
209
  }
208
210
 
209
211
  async getTransactionCount(...params) {
210
212
  // nonce is called during tx send, use it in rest api
211
213
  const { baseApiPath } = this
212
214
  const request = this.getTransactionCountRequest(...params)
213
- return this.handleJsonRPCResponse(await fetchRpcHttpRequest({ baseApiPath, body: request }))
215
+ return this.handleJsonRPCResponse(
216
+ await this.fetchRpcHttpRequest({ baseApiPath, body: request })
217
+ )
214
218
  }
215
219
  }
@@ -1,9 +1,11 @@
1
1
  import { bufferToHex } from '@exodus/ethereumjs/util'
2
+ import { safeString } from '@exodus/safe-string'
2
3
  import SolidityContract from '@exodus/solidity-contract'
3
4
  import EventEmitter from 'events/events.js'
4
5
  import io from 'socket.io-client'
5
6
 
6
7
  import { fromHexToString } from '../number-utils.js'
8
+ import { errorMessageToSafeHint } from './errors.js'
7
9
 
8
10
  export const RPC_REQUEST_TIMEOUT = 'RPC_REQUEST_TIMEOUT'
9
11
 
@@ -94,8 +96,11 @@ export default class ClarityServer extends EventEmitter {
94
96
  const result = response?.result
95
97
  const error = response?.error
96
98
  if (error || result === undefined) {
97
- const message = error?.message || error?.code || 'no result'
98
- throw new Error(`Bad rpc response: ${message}`)
99
+ const message = error?.message || error?.code || safeString`no result`
100
+
101
+ const revisedError = new Error(`Bad rpc response: ${message}`)
102
+ revisedError.hint = safeString`Bad rpc response: ${errorMessageToSafeHint(message)}`
103
+ throw revisedError
99
104
  }
100
105
 
101
106
  return result
@@ -0,0 +1,36 @@
1
+ import { safeString } from '@exodus/safe-string'
2
+
3
+ // Transplated from: https://github.com/ethereum/go-ethereum/blob/master/core/txpool/errors.go.
4
+ const TxPoolErrors = {
5
+ ALREADY_KNOWN: safeString`already known`,
6
+ INVALID_SENDER: safeString`invalid sender`,
7
+ UNDERPRICED: safeString`transaction underpriced`,
8
+ REPLACE_UNDERPRICED: safeString`replacement transaction underpriced`,
9
+ TX_GAS_PRICE_TOO_LOW: safeString`transaction gas price below minimum`,
10
+ ACCOUNT_LIMIT_EXCEEDED: safeString`account limit exceeded`,
11
+ GAS_LIMIT: safeString`exceeds block gas limit`,
12
+ NEGATIVE_VALUE: safeString`negative value`,
13
+ OVERSIZED_DATA: safeString`oversized data`,
14
+ TX_BLOB_LIMIT_EXCEEDED: safeString`transaction blob limit exceeded`,
15
+ ALREADY_RESERVED: safeString`address already reserved`,
16
+ INFLIGHT_TX_LIMIT_REACHED: safeString`in-flight transaction limit reached for delegated accounts`,
17
+ }
18
+
19
+ const DYNAMIC_RPC_ERRORS = {
20
+ TRANSACTION_TYPE_NOT_SUPPORTED: safeString`transaction type not supported`,
21
+ ONLY_REPLAY_PROTECTED_TRANSACTIONS: safeString`only replay-protected (EIP-155) transactions allowed over RPC`,
22
+ TRANSACTION_GAS_PRICE_BELOW_MINIMUM: safeString`transaction gas price below minimum`,
23
+ }
24
+
25
+ export const KNOWN_RPC_ERRORS = { ...TxPoolErrors, ...DYNAMIC_RPC_ERRORS }
26
+
27
+ export const tryMappingToRpcErrors = (errorMessage) => {
28
+ if (typeof errorMessage !== 'string') return
29
+
30
+ return Object.values(KNOWN_RPC_ERRORS)
31
+ .sort((a, b) => b.length - a.length)
32
+ .find((knownRpcError) => errorMessage.includes(knownRpcError))
33
+ }
34
+
35
+ export const errorMessageToSafeHint = (messageOrCode) =>
36
+ tryMappingToRpcErrors(messageOrCode) || messageOrCode