@exodus/ethereum-lib 2.11.0 → 2.12.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-lib",
3
- "version": "2.11.0",
3
+ "version": "2.12.1",
4
4
  "description": "Ethereum Library",
5
5
  "main": "src/index.js",
6
6
  "author": "Exodus Movement, Inc.",
@@ -24,5 +24,5 @@
24
24
  "peerDependencies": {
25
25
  "@exodus/assets": "8.0.x"
26
26
  },
27
- "gitHead": "84627634686049aa4d88d8b631af006dba571bb9"
27
+ "gitHead": "c83e89a956335bdf9a323a868027e22918a7d251"
28
28
  }
@@ -0,0 +1,128 @@
1
+ // extended (with metadata: decimals, name, symbol) erc20
2
+ // compiled from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/extensions/IERC20Metadata.sol with solcjs, compiler 0.8.0
3
+ // `solcjs --abi --base-path . extensions/IERC20Metadata.sol`
4
+
5
+ export default [
6
+ {
7
+ anonymous: false,
8
+ inputs: [
9
+ { indexed: true, internalType: 'address', name: 'owner', type: 'address' },
10
+ {
11
+ indexed: true,
12
+ internalType: 'address',
13
+ name: 'spender',
14
+ type: 'address',
15
+ },
16
+ { indexed: false, internalType: 'uint256', name: 'value', type: 'uint256' },
17
+ ],
18
+ name: 'Approval',
19
+ type: 'event',
20
+ },
21
+ {
22
+ anonymous: false,
23
+ inputs: [
24
+ { indexed: true, internalType: 'address', name: 'from', type: 'address' },
25
+ {
26
+ indexed: true,
27
+ internalType: 'address',
28
+ name: 'to',
29
+ type: 'address',
30
+ },
31
+ { indexed: false, internalType: 'uint256', name: 'value', type: 'uint256' },
32
+ ],
33
+ name: 'Transfer',
34
+ type: 'event',
35
+ },
36
+ {
37
+ inputs: [
38
+ { internalType: 'address', name: 'owner', type: 'address' },
39
+ {
40
+ internalType: 'address',
41
+ name: 'spender',
42
+ type: 'address',
43
+ },
44
+ ],
45
+ name: 'allowance',
46
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
47
+ stateMutability: 'view',
48
+ type: 'function',
49
+ },
50
+ {
51
+ inputs: [
52
+ { internalType: 'address', name: 'spender', type: 'address' },
53
+ {
54
+ internalType: 'uint256',
55
+ name: 'amount',
56
+ type: 'uint256',
57
+ },
58
+ ],
59
+ name: 'approve',
60
+ outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
61
+ stateMutability: 'nonpayable',
62
+ type: 'function',
63
+ },
64
+ {
65
+ inputs: [{ internalType: 'address', name: 'account', type: 'address' }],
66
+ name: 'balanceOf',
67
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
68
+ stateMutability: 'view',
69
+ type: 'function',
70
+ },
71
+ {
72
+ inputs: [],
73
+ name: 'decimals',
74
+ outputs: [{ internalType: 'uint8', name: '', type: 'uint8' }],
75
+ stateMutability: 'view',
76
+ type: 'function',
77
+ },
78
+ {
79
+ inputs: [],
80
+ name: 'name',
81
+ outputs: [{ internalType: 'string', name: '', type: 'string' }],
82
+ stateMutability: 'view',
83
+ type: 'function',
84
+ },
85
+ {
86
+ inputs: [],
87
+ name: 'symbol',
88
+ outputs: [{ internalType: 'string', name: '', type: 'string' }],
89
+ stateMutability: 'view',
90
+ type: 'function',
91
+ },
92
+ {
93
+ inputs: [],
94
+ name: 'totalSupply',
95
+ outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
96
+ stateMutability: 'view',
97
+ type: 'function',
98
+ },
99
+ {
100
+ inputs: [
101
+ { internalType: 'address', name: 'to', type: 'address' },
102
+ {
103
+ internalType: 'uint256',
104
+ name: 'amount',
105
+ type: 'uint256',
106
+ },
107
+ ],
108
+ name: 'transfer',
109
+ outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
110
+ stateMutability: 'nonpayable',
111
+ type: 'function',
112
+ },
113
+ {
114
+ inputs: [
115
+ { internalType: 'address', name: 'from', type: 'address' },
116
+ {
117
+ internalType: 'address',
118
+ name: 'to',
119
+ type: 'address',
120
+ },
121
+ { internalType: 'uint256', name: 'amount', type: 'uint256' },
122
+ ],
123
+ name: 'transferFrom',
124
+ outputs: [{ internalType: 'bool', name: '', type: 'bool' }],
125
+ stateMutability: 'nonpayable',
126
+ type: 'function',
127
+ },
128
+ ]
package/src/abi/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import ant from './ant'
2
2
  import cdai from './cdai'
3
3
  import dai from './dai'
4
+ import erc20 from './ierc20-extended'
4
5
  import gnt from './gnt'
5
6
  import kyberv2 from './kyberv2.js'
6
7
  import loomv2Swap from './loomv2-swap'
@@ -11,6 +12,7 @@ export default {
11
12
  ant,
12
13
  cdai,
13
14
  dai,
15
+ erc20,
14
16
  gnt,
15
17
  kyberv2,
16
18
  loomv2Swap,
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
+ fuelThreshold: '20000 nAVAX',
11
+ gasPriceEconomicalRate: 0.8,
12
+ gasPriceMinimumRate: 0.6,
13
+ },
14
+ 'gasPrice',
15
+ 'avalanchec'
16
+ )
@@ -5,7 +5,7 @@ export default new FeeData(
5
5
  gasPrice: '20 Gwei',
6
6
  max: '200 Gwei',
7
7
  min: '1 Gwei',
8
- erc20FuelThreshold: '2500000 Gwei',
8
+ fuelThreshold: '2500000 Gwei',
9
9
  gasPriceEconomicalRate: 0.8,
10
10
  gasPriceMinimumRate: 0.6,
11
11
  },
@@ -5,7 +5,7 @@ export default new FeeData(
5
5
  gasPrice: '75 Gwei',
6
6
  max: '250 Gwei',
7
7
  min: '1 Gwei',
8
- erc20FuelThreshold: '0.025 ETH',
8
+ fuelThreshold: '0.025 ETH',
9
9
  swapFee: '0.05 ETH',
10
10
  gasPriceEconomicalRate: 0.7,
11
11
  gasPriceMinimumRate: 0.5,
@@ -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'
@@ -6,7 +6,7 @@ export default new FeeData(
6
6
  gasPrice: '5 Gwei',
7
7
  max: '300 Gwei',
8
8
  min: '30 Gwei',
9
- erc20FuelThreshold: '2500000 Gwei',
9
+ fuelThreshold: '2500000 Gwei',
10
10
  gasPriceEconomicalRate: 0.8,
11
11
  gasPriceMinimumRate: 0.6,
12
12
  },
@@ -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,16 +12,20 @@ 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)
19
+ export const isEthereumLikeTokenByName = (assetName) => isEthereumLikeToken(assets[assetName])
18
20
  export const isToken = isEthereumLikeToken
19
21
 
20
22
  // All ethereum-like assets (native coins)
21
- export const isEthereumLikeAsset = (asset) => ETHEREUM_LIKE_ASSETS.includes(asset.name)
23
+ export const isEthereumLikeAsset = (asset) => isEthereumLikeAssetByName(asset.name)
24
+ export const isEthereumLikeAssetByName = (assetName) => ETHEREUM_LIKE_ASSETS.includes(assetName)
22
25
 
23
26
  // All ethereum-like assets and tokens
24
27
  export const isEthereumLike = (asset) => ETHEREUM_LIKE_ASSETS.includes(asset.baseAsset.name)
28
+ export const isEthereumLikeByName = (assetName) => isEthereumLike(assets[assetName])
25
29
 
26
30
  export function buffer2currency({ asset, value }) {
27
31
  return asset.currency.baseUnit(base10.encode(value)).toDefault()