@exodus/ethereum-api 8.8.0 → 8.9.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,28 @@
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.9.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.9.0...@exodus/ethereum-api@8.9.1) (2024-07-03)
7
+
8
+ **Note:** Version bump only for package @exodus/ethereum-api
9
+
10
+
11
+
12
+
13
+
14
+ ## [8.9.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.8.0...@exodus/ethereum-api@8.9.0) (2024-07-03)
15
+
16
+
17
+ ### Features
18
+
19
+ * dynamic evm factory ([#2659](https://github.com/ExodusMovement/assets/issues/2659)) ([0baae82](https://github.com/ExodusMovement/assets/commit/0baae82a7f53c8808c9cd2621cf04e9960568cc6))
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * **ethereum:** false dropped tx check ([#2707](https://github.com/ExodusMovement/assets/issues/2707)) ([d05a8c8](https://github.com/ExodusMovement/assets/commit/d05a8c895563fb13120956c0081db7ebe86195d6))
25
+
26
+
27
+
6
28
  ## [8.8.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.7.1...@exodus/ethereum-api@8.8.0) (2024-07-02)
7
29
 
8
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "8.8.0",
3
+ "version": "8.9.1",
4
4
  "description": "Ethereum Api",
5
5
  "main": "src/index.js",
6
6
  "files": [
@@ -20,6 +20,7 @@
20
20
  "lint:fix": "yarn lint --fix"
21
21
  },
22
22
  "dependencies": {
23
+ "@exodus/asset": "^1.2.0",
23
24
  "@exodus/asset-lib": "^4.2.0",
24
25
  "@exodus/assets": "^9.1.1",
25
26
  "@exodus/basic-utils": "^2.1.0",
@@ -48,7 +49,6 @@
48
49
  "ws": "^6.1.0"
49
50
  },
50
51
  "devDependencies": {
51
- "@exodus/assets": "^9.1.1",
52
52
  "@exodus/assets-testing": "^1.0.0",
53
53
  "@exodus/bsc-meta": "^1.2.2",
54
54
  "@exodus/ethereumarbone-meta": "^1.2.0",
@@ -66,5 +66,5 @@
66
66
  "type": "git",
67
67
  "url": "git+https://github.com/ExodusMovement/assets.git"
68
68
  },
69
- "gitHead": "2429beb0c513a4e0da8de7e4c070f0774e5f62c4"
69
+ "gitHead": "d52d315260d32e7a2fce15e32e7393813a02bc90"
70
70
  }
@@ -0,0 +1,162 @@
1
+ import { createMetaDef } from '@exodus/asset'
2
+ import { FeeData } from '@exodus/asset-lib'
3
+ import { createAssetFactory } from './create-asset'
4
+ import assert from 'minimalistic-assert'
5
+ import { UnitType } from '@exodus/currency'
6
+
7
+ // Move to ethereum-api for config base evm
8
+ export const createAssetPluginFactory = (config) => {
9
+ assert(config, 'config is required')
10
+ assert(config.chainId, 'chainId is required')
11
+ assert(config.meta, 'config.meta is required')
12
+ assert(config.meta.explorerUrl, 'meta.explorerUrl is required')
13
+
14
+ const chainId = config.chainId
15
+ const { explorerUrl, ...meta } = config.meta
16
+ const name = `evm_${chainId}`
17
+ const ticker = `EVM${chainId}`
18
+ const tokenType = `EVM_${chainId}_TOKEN`
19
+
20
+ const units = {
21
+ wei: 0,
22
+ Kwei: 3,
23
+ Mwei: 6,
24
+ Gwei: 9,
25
+ [ticker]: config.decimals || 18,
26
+ }
27
+
28
+ const currency = UnitType.create(units)
29
+
30
+ // adds lots of validation in config
31
+ const tokenOverrides = (token) => ({
32
+ ...token,
33
+ assetType: tokenType,
34
+ contract: token.addresses,
35
+ gasLimit: meta.gasLimit || 120e3,
36
+ })
37
+
38
+ const blockExplorer = {
39
+ addressUrl: (address) => `${explorerUrl}/address/${encodeURIComponent(address)}`,
40
+ txUrl: (txId) => `${explorerUrl}/tx/${encodeURIComponent(txId)}`,
41
+ }
42
+
43
+ const assetType = 'ETHEREUM_LIKE'
44
+ const { asset, assetsList } = createMetaDef({
45
+ assetParams: {
46
+ ...meta,
47
+ name,
48
+ ticker,
49
+ units,
50
+ chainId,
51
+ assetType,
52
+ blockExplorer,
53
+ },
54
+ tokensParams: meta.tokens || [],
55
+ tokenOverrides,
56
+ })
57
+
58
+ const resolveFeeDataConfigDefaults = () => {
59
+ const shared = {
60
+ tipGasPrice: '0 Gwei',
61
+ fuelThreshold: '0 Gwei',
62
+ gasPriceEconomicalRate: 0.8,
63
+ gasPriceMinimumRate: 0.6,
64
+ }
65
+
66
+ if (config.eip1559Enabled === true) {
67
+ assert(config.baseFeePerGas, 'baseFeePerGas is required')
68
+ const baseFeePerGas = currency.parse(config.baseFeePerGas)
69
+ const gasPrice = config.gasPrice ? currency.parse(config.gasPrice) : baseFeePerGas.mul(1.5)
70
+ return {
71
+ gasPrice: gasPrice.toBaseString({ unit: true }),
72
+ baseFeePerGas: baseFeePerGas.toBaseString({ unit: true }),
73
+ max: gasPrice.mul(5).toBaseString({ unit: true }),
74
+ min: gasPrice.div(5).toBaseString({ unit: true }),
75
+ eip1559Enabled: config.eip1559Enabled,
76
+ ...shared,
77
+ ...config.feeData,
78
+ }
79
+ }
80
+
81
+ if (config.eip1559Enabled === false) {
82
+ assert(config.gasPrice, 'gasPrice is required')
83
+ const gasPrice = currency.parse(config.gasPrice)
84
+ return {
85
+ gasPrice: gasPrice.toBaseString({ unit: true }),
86
+ max: gasPrice.mul(5).toBaseString({ unit: true }),
87
+ min: gasPrice.div(5).toBaseString({ unit: true }),
88
+ eip1559Enabled: config.eip1559Enabled,
89
+ ...shared,
90
+ ...config.feeData,
91
+ }
92
+ }
93
+
94
+ return {
95
+ gasPrice: '0.02 Gwei',
96
+ baseFeePerGas: '0.02 Gwei',
97
+ eip1559Enabled: true,
98
+ ...shared,
99
+ ...config.feeData,
100
+ }
101
+ }
102
+
103
+ const feeData = new FeeData({
104
+ config: resolveFeeDataConfigDefaults(),
105
+ mainKey: 'gasPrice',
106
+ currency: asset.currency,
107
+ })
108
+
109
+ const createAsset = createAssetFactory({
110
+ assetsList,
111
+ feeData,
112
+ ...config.plugin,
113
+ })
114
+
115
+ return { name, chainId, createAsset }
116
+ }
117
+
118
+ // https://docs.metamask.io/wallet/reference/wallet_addethereumchain/
119
+ export const fromAddEthereumChainParameterToFactoryParams = (params) => {
120
+ assert(params.chainId, 'chainId is required')
121
+ assert(params.chainName, 'chainName is required')
122
+ assert(params.rpcUrls?.[0], 'rpcUrls is required')
123
+ assert(params.blockExplorerUrls?.[0], 'blockExplorerUrls is required')
124
+ assert(params.nativeCurrency, 'nativeCurrency is required')
125
+ assert(params.nativeCurrency.symbol, 'nativeCurrency.symbol is required')
126
+ assert(params.nativeCurrency.decimals, 'nativeCurrency.decimals is required')
127
+
128
+ const chainId = parseInt(params.chainId.replace('0x', ''), 16)
129
+
130
+ // obvious coloring:)
131
+ const color =
132
+ params.color ||
133
+ '#' +
134
+ (chainId % 256).toString(16) +
135
+ ((chainId + 50) % 256).toString(16) +
136
+ ((chainId + 100) % 256).toString(16)
137
+ return {
138
+ chainId,
139
+ meta: {
140
+ explorerUrl: params.blockExplorerUrls[0],
141
+ displayName: params.chainName,
142
+ displayTicker: params.nativeCurrency.name || params.nativeCurrency.symbol,
143
+ primaryColor: color,
144
+ gradientColors: [color, color],
145
+ gradientCoords: {
146
+ x1: '0%',
147
+ y1: '0%',
148
+ x2: '100%',
149
+ y2: '100%',
150
+ },
151
+ },
152
+ plugin: {
153
+ serverUrl: params.rpcUrls?.[0],
154
+ confirmationsNumber: 5,
155
+ monitorType: 'no-history',
156
+ isTestnet: params.isTestnet,
157
+ },
158
+ baseFeePerGas: params.baseFeePerGas,
159
+ gasPrice: params.gasPrice,
160
+ eip1559Enabled: params.eip1559Enabled,
161
+ }
162
+ }
@@ -1,7 +1,7 @@
1
1
  import { connectAssetsList } from '@exodus/assets'
2
2
  import { pick } from '@exodus/basic-utils'
3
3
  import bip44Constants from '@exodus/bip44-constants/by-ticker'
4
- import { getBalances } from './get-balances'
4
+ import { getBalancesFactory } from './get-balances'
5
5
  import { ClarityMonitor } from './tx-log/clarity-monitor'
6
6
  import { EthereumNoHistoryMonitor } from './tx-log/ethereum-no-history-monitor'
7
7
  import { EthereumMonitor } from './tx-log/ethereum-monitor'
@@ -108,7 +108,12 @@ export const createAssetFactory = ({
108
108
  encodePublic: encodePublicFactory({ chainId, useEip1191ChainIdChecksum }),
109
109
  }
110
110
 
111
- const { createToken, getTokens } = createTokenFactory({ address, bip44, keys }, assets)
111
+ const getBalances = getBalancesFactory({ monitorType })
112
+
113
+ const { createToken, getTokens } = createTokenFactory(
114
+ { address, bip44, keys, getBalances },
115
+ assets
116
+ )
112
117
 
113
118
  const addressHasHistory = addressHasHistoryFactory({ server })
114
119
 
@@ -1,9 +1,8 @@
1
1
  import { isPolygonClaimUndelegate, isPolygonDelegate, isPolygonUndelegate } from './staking/matic'
2
2
  import { createContract } from '@exodus/ethereum-lib'
3
3
 
4
- import { getBalances } from './get-balances'
5
-
6
4
  import { createStakingApi } from './staking-api'
5
+ import assert from 'minimalistic-assert'
7
6
 
8
7
  const defaultTokenFeatures = {
9
8
  isMaxFeeAsset: true,
@@ -30,8 +29,9 @@ const getPolygonActivityTxs = ({ txs }) =>
30
29
  const getActivityTxs = ({ txs }) => txs.filter((tx) => !smallbalanceTx(tx))
31
30
 
32
31
  const getCreateBaseToken =
33
- (props) =>
32
+ ({ getBalances, ...props }) =>
34
33
  ({ name, contract, features, ...tokenDef }) => {
34
+ assert(getBalances, 'getBalances is required')
35
35
  const tokenSpecificFeatures = name === 'polygon' ? { staking: {} } : {}
36
36
  const tokenSpecificApiFunctions =
37
37
  name === 'polygon' ? { staking: createStakingApi({ network: name }) } : {}
@@ -10,7 +10,7 @@ export const reasons = {
10
10
  }
11
11
 
12
12
  const MAX_HINT_LENGTH = 100
13
- // TODO: move this to be an generic error for all assets
13
+ // TODO: move this to be a generic error for all assets
14
14
  export class EthLikeError extends Error {
15
15
  #hintStack
16
16
 
@@ -18,7 +18,6 @@ export class EthLikeError extends Error {
18
18
  * Creates an instance of EthLikeError.
19
19
  *
20
20
  * @param {string} message - Standard error message.
21
- * @param {string} tag - Tag associated with the error.
22
21
  * @param {string} reason - A constant indicating the generic failure. Must not contain any sensitive information such as private keys, transaction IDs, or wallet addresses.
23
22
  * @param {string} hint - A hint to help the user understand the error. Must not contain any sensitive information such as private keys, transaction IDs, or wallet addresses.
24
23
  */
@@ -47,8 +46,10 @@ export class EthLikeError extends Error {
47
46
 
48
47
  // Define regex patterns for sensitive information
49
48
  const sensitivePatterns = [
50
- /(?:0x)?[\dA-Fa-f]{64}/g, // Pattern for transaction hashes (hex string of 64 characters, optional 0x prefix)
51
- /(?:0x)?[\dA-Fa-f]{40}/g, // Pattern for wallet addresses (hex string of 40 characters, optional 0x prefix)
49
+ /(?:0x)?[\dA-Fa-f]{20,}/g, // Pattern for hex values of 20 or more characters
50
+ /(?:[A-Za-z]{3,20}\s+){11}[A-Za-z]{3,20}/g, // Pattern for 12-word phrases
51
+ /[1-9A-HJ-NP-Za-km-z]{50,}/g, // Pattern for base58 strings of 50 or more characters
52
+ /(?:[\d+/A-Za-z]{4}){3,}(?:[\d+/A-Za-z]{2}==|[\d+/A-Za-z]{3}=|[\d+/A-Za-z]{4})/g, // Pattern for base64 strings of 12 or more characters
52
53
  ]
53
54
 
54
55
  let filteredHint = hint
@@ -1,6 +1,8 @@
1
1
  import { isRpcBalanceAsset } from '@exodus/ethereum-lib'
2
2
 
3
3
  import { get } from 'lodash'
4
+ import assert from 'minimalistic-assert'
5
+
4
6
  import {
5
7
  getBalanceFromAccountState,
6
8
  getBalanceFromTxLog,
@@ -30,36 +32,40 @@ const getUnstaked = ({ accountState, asset }) => {
30
32
  * @param accountState the account state when the balance is loaded from RPC
31
33
  * @returns {{balance}|null} an object with the balance or null if the balance is unknown
32
34
  */
33
- export const getBalances = ({ asset, txLog, accountState }) => {
34
- const unconfirmedReceived = getUnconfirmedReceivedBalance({ asset, txLog })
35
- const unconfirmedSent = getUnconfirmedSentBalance({ asset, txLog })
35
+ export const getBalancesFactory = ({ monitorType }) => {
36
+ assert(monitorType, 'monitorType is required')
37
+ return ({ asset, txLog, accountState }) => {
38
+ const unconfirmedReceived = getUnconfirmedReceivedBalance({ asset, txLog })
39
+ const unconfirmedSent = getUnconfirmedSentBalance({ asset, txLog })
36
40
 
37
- // balance from txLog / rpc is considered to be total
38
- const balanceWithoutUnconfirmedSent = isRpcBalanceAsset(asset)
39
- ? getBalanceFromAccountState({ asset, accountState }).sub(unconfirmedSent)
40
- : getBalanceFromTxLog({ txLog, asset })
41
+ // balance from txLog / rpc is considered to be total
42
+ const balanceWithoutUnconfirmedSent =
43
+ monitorType === 'no-history' || isRpcBalanceAsset(asset)
44
+ ? getBalanceFromAccountState({ asset, accountState }).sub(unconfirmedSent)
45
+ : getBalanceFromTxLog({ txLog, asset })
41
46
 
42
- const staked = getStaked({ asset, accountState })
43
- const unstaking = getUnstaking({ asset, accountState })
44
- const unstaked = getUnstaked({ asset, accountState })
45
- const total = balanceWithoutUnconfirmedSent
46
- const spendable = balanceWithoutUnconfirmedSent
47
- .sub(unconfirmedReceived)
48
- .sub(staked)
49
- .sub(unstaking)
50
- .sub(unstaked)
47
+ const staked = getStaked({ asset, accountState })
48
+ const unstaking = getUnstaking({ asset, accountState })
49
+ const unstaked = getUnstaked({ asset, accountState })
50
+ const total = balanceWithoutUnconfirmedSent
51
+ const spendable = balanceWithoutUnconfirmedSent
52
+ .sub(unconfirmedReceived)
53
+ .sub(staked)
54
+ .sub(unstaking)
55
+ .sub(unstaked)
51
56
 
52
- return {
53
- // new
54
- spendable,
55
- total,
56
- staked,
57
- unstaking,
58
- unstaked,
59
- unconfirmedReceived,
60
- unconfirmedSent,
61
- // legacy
62
- balance: total,
63
- spendableBalance: spendable,
57
+ return {
58
+ // new
59
+ spendable,
60
+ total,
61
+ staked,
62
+ unstaking,
63
+ unstaked,
64
+ unconfirmedReceived,
65
+ unconfirmedSent,
66
+ // legacy
67
+ balance: total,
68
+ spendableBalance: spendable,
69
+ }
64
70
  }
65
71
  }
package/src/index.js CHANGED
@@ -14,3 +14,7 @@ export * from './number-utils'
14
14
  export { reasons as errorReasons, withErrorReason, EthLikeError } from './error-wrapper'
15
15
  export { txSendFactory, getFeeInfo } from './tx-send'
16
16
  export { createAssetFactory } from './create-asset'
17
+ export {
18
+ createAssetPluginFactory,
19
+ fromAddEthereumChainParameterToFactoryParams,
20
+ } from './create-asset-plugin-factory'
@@ -123,8 +123,8 @@ export class EthereumMonitor extends BaseMonitor {
123
123
  // FIXME: Fetching unconfirmed txs from node helps to avoid
124
124
  // flagging on-chain confirmed txs as dropped in the wallet.
125
125
  // Once the real bug is found, remove this logic
126
- const response = await this.server.getTransactionByHash(tx.txId)
127
- const isTxConfirmed = response?.result?.blockNumber !== null
126
+ const rpcTx = await this.server.getTransactionByHash(tx.txId)
127
+ const isTxConfirmed = !!rpcTx?.blockNumber
128
128
  this.logger.warn(`tx ${tx.txId} confirmed: ${isTxConfirmed}`)
129
129
  await updateTx(tx, assetName, { isTxConfirmed })
130
130
  }