@exodus/ethereum-lib 2.10.8 → 2.12.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-lib",
3
- "version": "2.10.8",
3
+ "version": "2.12.0",
4
4
  "description": "Ethereum Library",
5
5
  "main": "src/index.js",
6
6
  "author": "Exodus Movement, Inc.",
@@ -23,6 +23,5 @@
23
23
  },
24
24
  "peerDependencies": {
25
25
  "@exodus/assets": "8.0.x"
26
- },
27
- "gitHead": "cf8f7f40a6fce5c1513da63ca736f7fbe9d05cbb"
26
+ }
28
27
  }
package/src/constants.js CHANGED
@@ -1,4 +1,10 @@
1
- export const CHAIN_IDS = { ethereum: 1, ethereumclassic: 61, bsc: 56, matic: 137 }
1
+ export const CHAIN_IDS = {
2
+ ethereum: 1,
3
+ ethereumclassic: 61,
4
+ bsc: 56,
5
+ matic: 137,
6
+ avalanchec: 43114,
7
+ }
2
8
  export const MIN_GASPRICE = 1e9 // 1 gwei
3
9
  export const DEFAULT_FEE_MONITOR_INTERVAL = '1m'
4
10
  export const CONFIRMATIONS_NUMBER = {
@@ -6,7 +12,13 @@ export const CONFIRMATIONS_NUMBER = {
6
12
  ethereumclassic: 5000,
7
13
  bsc: 15,
8
14
  matic: 355,
15
+ avalanchec: 30,
9
16
  }
10
- export const ETHEREUM_LIKE_ASSETS = ['bsc', 'ethereum', 'ethereumclassic', 'matic']
11
- export const ETHEREUM_LIKE_TOKEN_TYPES = ['ETHEREUM_ERC20', 'BSC_BEP20', 'MATIC_ERC20']
17
+ export const ETHEREUM_LIKE_ASSETS = ['bsc', 'ethereum', 'ethereumclassic', 'matic', 'avalanchec']
18
+ export const ETHEREUM_LIKE_TOKEN_TYPES = [
19
+ 'ETHEREUM_ERC20',
20
+ 'BSC_BEP20',
21
+ 'MATIC_ERC20',
22
+ 'AVAX_ERC20',
23
+ ]
12
24
  export const BUMP_RATE = 1.2
@@ -0,0 +1,16 @@
1
+ import { FeeData } from '@exodus/asset-lib'
2
+
3
+ export default new FeeData(
4
+ {
5
+ // https://snowtrace.io/chart/gasprice
6
+ // https://stats.avax.network/dashboard/c-chain-activity/
7
+ gasPrice: '225 nAVAX',
8
+ max: '1000 nAVAX',
9
+ min: '25 nAVAX',
10
+ erc20FuelThreshold: '20000 nAVAX',
11
+ gasPriceEconomicalRate: 0.8,
12
+ gasPriceMinimumRate: 0.6,
13
+ },
14
+ 'gasPrice',
15
+ 'avalanchec'
16
+ )
@@ -2,3 +2,4 @@ export { default as ethereum } from './ethereum'
2
2
  export { default as ethereumclassic } from './ethereumclassic'
3
3
  export { default as bsc } from './bsc'
4
4
  export { default as matic } from './polygon'
5
+ export { default as avalanchec } from './avalanchec'
@@ -1,18 +1,18 @@
1
1
  // A super-selector that returns all the current data needed for a tick of the ETH monitor.
2
2
 
3
3
  export default function getDeriveDataNeededForTick({
4
- getAccountStateSelector,
5
- getConfirmationsNumberSelector,
6
- getReceiveAddressSelector,
4
+ getAccountState,
5
+ getConfirmationsNumber,
6
+ getReceiveAddress,
7
7
  }) {
8
- return function({ state, assetSource }) {
9
- const { asset: assetName, walletAccount } = assetSource
8
+ return async function({ assetName, walletAccount }) {
9
+ const receiveAddress = await getReceiveAddress({ assetName, walletAccount })
10
+ const currentAccountState = await getAccountState({ assetName, walletAccount })
11
+ const minimumConfirmations = await getConfirmationsNumber({ assetName })
10
12
  return {
11
- ourWalletAddress: getReceiveAddressSelector(state)(assetName, walletAccount)
12
- .toString()
13
- .toLowerCase(),
14
- currentAccountState: getAccountStateSelector(state)(assetName, walletAccount),
15
- minimumConfirmations: getConfirmationsNumberSelector(state)(assetName),
13
+ ourWalletAddress: receiveAddress.toLowerCase(),
14
+ currentAccountState,
15
+ minimumConfirmations,
16
16
  }
17
17
  }
18
18
  }
@@ -3,17 +3,15 @@ import getSenderNonceKey from './get-sender-nonce-key'
3
3
 
4
4
  const UNCONFIRMED_TX_LIMIT = ms('5m')
5
5
 
6
- export default function getDeriveTransactionsToCheck({ getTxLogSelector }) {
7
- return ({ state, assetSource, tokens, ourWalletAddress }) => {
6
+ export default function getDeriveTransactionsToCheck({ getTxHistory }) {
7
+ return async ({ assetName: _assetName, walletAccount, tokens, ourWalletAddress }) => {
8
8
  const pendingTransactionsToCheck = {}
9
9
  const pendingTransactionsGroupedByAddressAndNonce = {}
10
10
  const simulatedTransactions = {}
11
11
  const now = Date.now()
12
- const getTxLog = getTxLogSelector(state)
13
- const { walletAccount, asset: _assetName } = assetSource
14
12
 
15
13
  for (const assetName of [_assetName, ...tokens.map(({ name }) => name)]) {
16
- const txSet = getTxLog(assetName, walletAccount)
14
+ const txSet = await getTxHistory({ assetName, walletAccount })
17
15
  for (const tx of txSet) {
18
16
  // ERC20 sends have an entry in ETH and one in the ERC20 log so we just want the ETH one
19
17
  if (
@@ -99,6 +99,58 @@ export const calculateBumpedGasPrice = ({ baseAsset, tx, currentGasPrice, eip155
99
99
  : { bumpedGasPrice }
100
100
  }
101
101
 
102
+ export const canAccelerateTx = ({
103
+ tx,
104
+ assets,
105
+ getFeeData,
106
+ activeWalletAccount,
107
+ getPersonalNoteByTxId,
108
+ getIsRbfEnabled,
109
+ getIsEnoughBalanceToAccelerate,
110
+ getTxLog,
111
+ getIsExchangeTx,
112
+ }) => {
113
+ const assetName = tx.coinName
114
+ const baseAsset = assets[assetName].baseAsset
115
+ const baseAssetName = baseAsset.name
116
+ if (!getIsRbfEnabled(assetName)) return wrapResponseToObject({ errorMessage: 'rbf is disabled' })
117
+ if (!isEthereumLike(assets[assetName]))
118
+ return wrapResponseToObject({ errorMessage: `not an ETH/ERC20/BSC/BEP20 asset supplied` })
119
+ if (!tx.pending || !tx.sent)
120
+ return wrapResponseToObject({ errorMessage: 'can not bump a confirmed or received TX' })
121
+ if (!tx.data || !(tx.data.gasLimit || tx.data.tipGasPrice))
122
+ return wrapResponseToObject({ errorMessage: 'data object is missing or corrupted' })
123
+ const isExchangeTx = getIsExchangeTx(tx.txId)
124
+ if (isExchangeTx) return wrapResponseToObject({ errorMessage: 'can not bump an exchange TX' })
125
+ const personalNote = getPersonalNoteByTxId(tx.txId)
126
+ if (personalNote && personalNote.dapp)
127
+ return wrapResponseToObject({ errorMessage: 'can not bump a dapp TX' })
128
+ if (isQueuedPendingTx(tx, baseAssetName, activeWalletAccount, getTxLog))
129
+ return wrapResponseToObject({ errorMessage: 'there is a stuck TX with lower nonce' })
130
+
131
+ const { gasPrice: currentGasPrice, gasPriceMinimumRate, eip1559Enabled } = getFeeData(assetName)
132
+ // converting to the smallest unit to avoid rounding errors
133
+ if (
134
+ calculateTxGasPrice(tx)
135
+ .to('wei')
136
+ .gte(currentGasPrice.mul(gasPriceMinimumRate).to('wei'))
137
+ )
138
+ return wrapResponseToObject({ errorMessage: 'the used gas price is still high enough' })
139
+
140
+ const { bumpedGasPrice: gasPriceToUse } = calculateBumpedGasPrice({
141
+ baseAsset,
142
+ tx,
143
+ currentGasPrice,
144
+ eip1559Enabled,
145
+ })
146
+ const replacementFee = gasPriceToUse.mul(tx.data.gasLimit)
147
+ const extraEthNeeded = replacementFee.sub(tx.feeAmount)
148
+ if (!getIsEnoughBalanceToAccelerate(activeWalletAccount, baseAssetName, extraEthNeeded))
149
+ return wrapResponseToObject({ errorMessage: 'insufficient funds' })
150
+
151
+ return wrapResponseToObject({ bumpType: BumpType.RBF })
152
+ }
153
+
102
154
  export default (
103
155
  getFeeDataSelector,
104
156
  getFeeSelector,
@@ -128,48 +180,17 @@ export default (
128
180
  getIsEnoughBalanceToAccelerate,
129
181
  getTxLog,
130
182
  getIsExchangeTx
131
- ) => (tx) => {
132
- const assetName = tx.coinName
133
- const baseAsset = assets[assetName].baseAsset
134
- const baseAssetName = baseAsset.name
135
- if (!getIsRbfEnabled(assetName))
136
- return wrapResponseToObject({ errorMessage: 'rbf is disabled' })
137
- if (!isEthereumLike(assets[assetName]))
138
- return wrapResponseToObject({ errorMessage: `not an ETH/ERC20/BSC/BEP20 asset supplied` })
139
- if (!tx.pending || !tx.sent)
140
- return wrapResponseToObject({ errorMessage: 'can not bump a confirmed or received TX' })
141
- if (!tx.data || !(tx.data.gasLimit || tx.data.tipGasPrice))
142
- return wrapResponseToObject({ errorMessage: 'data object is missing or corrupted' })
143
- const isExchangeTx = getIsExchangeTx(tx.txId)
144
- if (isExchangeTx) return wrapResponseToObject({ errorMessage: 'can not bump an exchange TX' })
145
- const personalNote = getPersonalNoteByTxId(tx.txId)
146
- if (personalNote && personalNote.dapp)
147
- return wrapResponseToObject({ errorMessage: 'can not bump a dapp TX' })
148
- if (isQueuedPendingTx(tx, baseAssetName, activeWalletAccount, getTxLog))
149
- return wrapResponseToObject({ errorMessage: 'there is a stuck TX with lower nonce' })
150
-
151
- const { gasPrice: currentGasPrice, gasPriceMinimumRate, eip1559Enabled } = getFeeData(
152
- assetName
153
- )
154
- // converting to the smallest unit to avoid rounding errors
155
- if (
156
- calculateTxGasPrice(tx)
157
- .to('wei')
158
- .gte(currentGasPrice.mul(gasPriceMinimumRate).to('wei'))
159
- )
160
- return wrapResponseToObject({ errorMessage: 'the used gas price is still high enough' })
161
-
162
- const { bumpedGasPrice: gasPriceToUse } = calculateBumpedGasPrice({
163
- baseAsset,
183
+ ) => (tx) =>
184
+ canAccelerateTx({
164
185
  tx,
165
- currentGasPrice,
166
- eip1559Enabled,
186
+ assets,
187
+ getFeeData,
188
+ getFee,
189
+ activeWalletAccount,
190
+ getPersonalNoteByTxId,
191
+ getIsRbfEnabled,
192
+ getIsEnoughBalanceToAccelerate,
193
+ getTxLog,
194
+ getIsExchangeTx,
167
195
  })
168
- const replacementFee = gasPriceToUse.mul(tx.data.gasLimit)
169
- const extraEthNeeded = replacementFee.sub(tx.feeAmount)
170
- if (!getIsEnoughBalanceToAccelerate(activeWalletAccount, baseAssetName, extraEthNeeded))
171
- return wrapResponseToObject({ errorMessage: 'insufficient funds' })
172
-
173
- return wrapResponseToObject({ bumpType: BumpType.RBF })
174
- }
175
196
  )
@@ -1,5 +1,9 @@
1
1
  import { createSelector } from 'reselect'
2
2
 
3
+ export const isEnoughBalanceToAccelerate = (totalBalance, unconfirmedBalance, extraEthNeeded) => {
4
+ return totalBalance.sub(unconfirmedBalance).gte(extraEthNeeded)
5
+ }
6
+
3
7
  export default (getWalletAccountBalancesSelector, getUnconfirmedEthereumBalanceSelector) =>
4
8
  createSelector(
5
9
  getWalletAccountBalancesSelector,
@@ -15,6 +19,6 @@ export default (getWalletAccountBalancesSelector, getUnconfirmedEthereumBalanceS
15
19
  walletAccount,
16
20
  })
17
21
 
18
- return totalBalance.sub(unconfirmedBalance).gte(extraEthNeeded)
22
+ return isEnoughBalanceToAccelerate(totalBalance, unconfirmedBalance, extraEthNeeded)
19
23
  }
20
24
  )
@@ -12,6 +12,7 @@ const base16 = baseX('0123456789abcdef')
12
12
  export const isEthereumToken = (asset) => asset.assetType === 'ETHEREUM_ERC20'
13
13
  export const isBscToken = (asset) => asset.assetType === 'BSC_BEP20'
14
14
  export const isPolygonToken = (asset) => asset.assetType === 'MATIC_ERC20'
15
+ export const isAvalancheToken = (asset) => asset.assetType === 'AVAX_ERC20'
15
16
 
16
17
  // All ethereum-like tokens
17
18
  export const isEthereumLikeToken = (asset) => ETHEREUM_LIKE_TOKEN_TYPES.includes(asset.assetType)
package/LICENSE.md DELETED
File without changes