@exodus/ethereum-api 8.8.0 → 8.9.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,20 @@
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.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.8.0...@exodus/ethereum-api@8.9.0) (2024-07-03)
7
+
8
+
9
+ ### Features
10
+
11
+ * dynamic evm factory ([#2659](https://github.com/ExodusMovement/assets/issues/2659)) ([0baae82](https://github.com/ExodusMovement/assets/commit/0baae82a7f53c8808c9cd2621cf04e9960568cc6))
12
+
13
+
14
+ ### Bug Fixes
15
+
16
+ * **ethereum:** false dropped tx check ([#2707](https://github.com/ExodusMovement/assets/issues/2707)) ([d05a8c8](https://github.com/ExodusMovement/assets/commit/d05a8c895563fb13120956c0081db7ebe86195d6))
17
+
18
+
19
+
6
20
  ## [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
21
 
8
22
 
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.0",
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": "237c799e9bc8df6574be95056a999896e2503290"
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 }) } : {}
@@ -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
  }