@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 +2 -2
- package/src/abi/ierc20-extended.js +128 -0
- package/src/abi/index.js +2 -0
- package/src/constants.js +15 -3
- package/src/fee-data/avalanchec.js +16 -0
- package/src/fee-data/bsc.js +1 -1
- package/src/fee-data/ethereum.js +1 -1
- package/src/fee-data/index.js +1 -0
- package/src/fee-data/polygon.js +1 -1
- package/src/selectors/get-can-accelerate-tx-factory.js +63 -42
- package/src/selectors/get-is-enough-balance-to-accelerate-factory.js +5 -1
- package/src/utils/index.js +5 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-lib",
|
|
3
|
-
"version": "2.
|
|
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": "
|
|
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 = {
|
|
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 = [
|
|
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
|
+
)
|
package/src/fee-data/bsc.js
CHANGED
package/src/fee-data/ethereum.js
CHANGED
package/src/fee-data/index.js
CHANGED
package/src/fee-data/polygon.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
166
|
-
|
|
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
|
|
22
|
+
return isEnoughBalanceToAccelerate(totalBalance, unconfirmedBalance, extraEthNeeded)
|
|
19
23
|
}
|
|
20
24
|
)
|
package/src/utils/index.js
CHANGED
|
@@ -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) =>
|
|
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()
|