@exodus/ethereum-api 7.0.18 → 7.2.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 +10 -6
- package/src/allowance/constants.js +3 -2
- package/src/allowance/index.js +90 -84
- package/src/ens/addresses.js +3 -1
- package/src/eth-like-util.js +6 -5
- package/src/etherscan/account.js +2 -2
- package/src/etherscan/index.js +15 -35
- package/src/etherscan/proxy.js +3 -3
- package/src/etherscan/request.js +1 -1
- package/src/etherscan/ws.js +2 -2
- package/src/exodus-eth-server/api-coin-nodes.js +4 -3
- package/src/exodus-eth-server/api.js +6 -4
- package/src/exodus-eth-server/clarity.js +10 -3
- package/src/exodus-eth-server/ws.js +13 -1
- package/src/fee-monitor/ethereumarbnova.js +4 -1
- package/src/fee-monitor/ethereumarbone.js +4 -1
- package/src/fee-monitor/ethereumsepolia.js +18 -0
- package/src/fee-monitor/index.js +1 -0
- package/src/fee-monitor/optimism.js +4 -1
- package/src/gas-estimation.js +3 -1
- package/src/get-balances.js +2 -1
- package/src/simulate-tx/simulate-eth-tx.js +4 -3
- package/src/staking/ethereum/api.js +8 -8
- package/src/staking/ethereum/service.js +33 -35
- package/src/staking/ethereum/staking-utils.js +3 -5
- package/src/staking/fantom-staking.js +2 -2
- package/src/staking/matic/api.js +2 -3
- package/src/staking/matic/service.js +34 -42
- package/src/staking/staking-provider-client.js +3 -3
- package/src/tx-log/clarity-monitor.js +14 -9
- package/src/tx-log/clarity-utils/get-derive-data-needed-for-tick.js +1 -1
- package/src/tx-log/clarity-utils/get-log-items-from-server-tx.js +4 -1
- package/src/tx-log/ethereum-monitor.js +8 -11
- package/src/tx-log/ethereum-no-history-monitor.js +13 -11
- package/src/tx-log/monitor-utils/check-pending-transactions.js +2 -0
- package/src/tx-log/monitor-utils/exclude-unchanged-token-balances.js +2 -3
- package/src/tx-log/monitor-utils/get-derive-data-needed-for-tick.js +1 -1
- package/src/tx-log/monitor-utils/get-derive-transactions-to-check.js +2 -1
- package/src/tx-log/monitor-utils/get-log-items-from-server-tx.js +5 -2
- package/src/tx-log/ws-updates.js +1 -1
- package/src/websocket/index.js +5 -4
- package/LICENSE.md +0 -0
- package/src/tx-log/monitor-utils/types.js +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "7.0
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"description": "Ethereum Api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -19,12 +19,12 @@
|
|
|
19
19
|
"lint:fix": "yarn lint --fix"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@exodus/asset-lib": "^
|
|
22
|
+
"@exodus/asset-lib": "^4.1.0",
|
|
23
23
|
"@exodus/crypto": "^1.0.0-rc.0",
|
|
24
24
|
"@exodus/currency": "^2.1.3",
|
|
25
25
|
"@exodus/ethereum-lib": "^4.2.7",
|
|
26
26
|
"@exodus/ethereum-meta": "^1.2.0",
|
|
27
|
-
"@exodus/ethereumjs-util": "^7.1.0-exodus.
|
|
27
|
+
"@exodus/ethereumjs-util": "^7.1.0-exodus.7",
|
|
28
28
|
"@exodus/fetch": "^1.3.0-beta.4",
|
|
29
29
|
"@exodus/models": "^11.0.0",
|
|
30
30
|
"@exodus/simple-retry": "^0.0.6",
|
|
@@ -41,12 +41,16 @@
|
|
|
41
41
|
"ws": "^6.1.0"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@exodus/assets": "9.1.
|
|
45
|
-
"@exodus/assets-base": "^8.1.14",
|
|
44
|
+
"@exodus/assets": "^9.1.1",
|
|
46
45
|
"@exodus/assets-testing": "^1.0.0",
|
|
46
|
+
"@exodus/bsc-meta": "^1.1.2",
|
|
47
|
+
"@exodus/ethereumarbone-meta": "^1.1.5",
|
|
48
|
+
"@exodus/ethereumgoerli-meta": "^1.0.3",
|
|
49
|
+
"@exodus/fantommainnet-meta": "^1.0.5",
|
|
50
|
+
"@exodus/rootstock-meta": "^1.0.5",
|
|
47
51
|
"bignumber.js": "9.0.1",
|
|
48
52
|
"cross-fetch": "^3.1.5",
|
|
49
53
|
"delay": "4.0.1"
|
|
50
54
|
},
|
|
51
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "4c4d7567f9c6d24acf3b48d2b23e0c23b15b45aa"
|
|
52
56
|
}
|
|
@@ -8,7 +8,8 @@ export const ZERO_ALLOWANCE_ASSETS = [
|
|
|
8
8
|
'decentraland',
|
|
9
9
|
'dent',
|
|
10
10
|
'lidodao',
|
|
11
|
+
'enjincoin',
|
|
11
12
|
]
|
|
12
13
|
|
|
13
|
-
export const APPROVAL_GAS_LIMIT =
|
|
14
|
-
export const ALLOWANCE_TX_TIMEOUT =
|
|
14
|
+
export const APPROVAL_GAS_LIMIT = 60_000
|
|
15
|
+
export const ALLOWANCE_TX_TIMEOUT = 60_000 * 5
|
package/src/allowance/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { fetchGasLimit
|
|
1
|
+
import { fetchGasLimit } from '../gas-estimation'
|
|
2
|
+
import { getServer } from '../exodus-eth-server'
|
|
2
3
|
import { ALLOWANCE_TX_TIMEOUT, APPROVAL_GAS_LIMIT, ZERO_ALLOWANCE_ASSETS } from './constants'
|
|
3
4
|
|
|
4
|
-
export { APPROVAL_GAS_LIMIT, ZERO_ALLOWANCE_ASSETS }
|
|
5
|
-
|
|
6
5
|
export const isZeroAllowanceAsset = (assetName) => ZERO_ALLOWANCE_ASSETS.includes(assetName)
|
|
7
6
|
|
|
8
7
|
export async function getSpendingAllowance({ asset, fromAddress, spenderAddress }) {
|
|
@@ -28,94 +27,101 @@ export async function isSpendingApprovalRequired({
|
|
|
28
27
|
return tokenAmount.gt(allowance)
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
export const createApprove =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
asset,
|
|
36
|
-
feeData,
|
|
37
|
-
feeAmount,
|
|
38
|
-
approveAmount,
|
|
39
|
-
gasLimit,
|
|
40
|
-
txInput: preTxInput,
|
|
41
|
-
}) => {
|
|
42
|
-
const baseAsset = asset.baseAsset
|
|
43
|
-
const txInput =
|
|
44
|
-
preTxInput ?? asset.contract.approve.build(spenderAddress, approveAmount.toBaseString())
|
|
45
|
-
|
|
46
|
-
const toAddress = asset.contract.address
|
|
47
|
-
const gasLimitOptions = {
|
|
48
|
-
asset: baseAsset,
|
|
49
|
-
amount: baseAsset.currency.ZERO,
|
|
30
|
+
export const createApprove =
|
|
31
|
+
({ sendTx }) =>
|
|
32
|
+
async ({
|
|
33
|
+
walletAccount,
|
|
50
34
|
fromAddress,
|
|
51
|
-
|
|
52
|
-
|
|
35
|
+
spenderAddress,
|
|
36
|
+
asset,
|
|
53
37
|
feeData,
|
|
54
|
-
isContract: true,
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
gasLimit = gasLimit || (await fetchGasLimit(gasLimitOptions))
|
|
58
|
-
|
|
59
|
-
const txData = await sendTx({
|
|
60
|
-
asset: baseAsset.name,
|
|
61
|
-
walletAccount,
|
|
62
|
-
receiver: {
|
|
63
|
-
address: toAddress,
|
|
64
|
-
amount: baseAsset.currency.ZERO,
|
|
65
|
-
},
|
|
66
|
-
txInput,
|
|
67
|
-
gasPrice: feeData.gasPrice,
|
|
68
|
-
gasLimit,
|
|
69
38
|
feeAmount,
|
|
70
|
-
|
|
71
|
-
|
|
39
|
+
approveAmount,
|
|
40
|
+
gasLimit,
|
|
41
|
+
txInput: preTxInput,
|
|
42
|
+
}) => {
|
|
43
|
+
const baseAsset = asset.baseAsset
|
|
44
|
+
const txInput =
|
|
45
|
+
preTxInput ?? asset.contract.approve.build(spenderAddress, approveAmount.toBaseString())
|
|
46
|
+
|
|
47
|
+
const toAddress = asset.contract.address
|
|
48
|
+
const gasLimitOptions = {
|
|
49
|
+
asset: baseAsset,
|
|
50
|
+
amount: baseAsset.currency.ZERO,
|
|
51
|
+
fromAddress,
|
|
52
|
+
toAddress,
|
|
53
|
+
txInput,
|
|
54
|
+
feeData,
|
|
55
|
+
isContract: true,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
gasLimit = gasLimit || (await fetchGasLimit(gasLimitOptions))
|
|
59
|
+
|
|
60
|
+
const txData = await sendTx({
|
|
61
|
+
asset: baseAsset.name,
|
|
62
|
+
walletAccount,
|
|
63
|
+
receiver: {
|
|
64
|
+
address: toAddress,
|
|
65
|
+
amount: baseAsset.currency.ZERO,
|
|
66
|
+
},
|
|
67
|
+
txInput,
|
|
68
|
+
gasPrice: feeData.gasPrice,
|
|
69
|
+
gasLimit,
|
|
70
|
+
feeAmount,
|
|
71
|
+
silent: true,
|
|
72
|
+
})
|
|
72
73
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
if (!txData || !txData.txId) {
|
|
75
|
+
throw new Error(`Failed to approve ${asset.displayTicker} - ${spenderAddress}`)
|
|
76
|
+
}
|
|
76
77
|
|
|
77
|
-
|
|
78
|
-
}
|
|
78
|
+
return txData
|
|
79
|
+
}
|
|
79
80
|
|
|
80
|
-
const createSendApprovalAndWatchConfirmation =
|
|
81
|
-
|
|
82
|
-
) => {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
81
|
+
const createSendApprovalAndWatchConfirmation =
|
|
82
|
+
({ sendTx, watchTxConfirmation }) =>
|
|
83
|
+
async (data) => {
|
|
84
|
+
// To change the approve amount you first have to reduce the addresses`
|
|
85
|
+
// allowance to zero by calling `approve(_spender, 0)` if it is not
|
|
86
|
+
// already 0 to mitigate the race condition described here:
|
|
87
|
+
// see: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
|
|
88
|
+
// see: https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#code
|
|
89
|
+
const approve = createApprove({ sendTx })
|
|
90
|
+
const { txId } = await approve(data)
|
|
91
|
+
await watchTxConfirmation(
|
|
92
|
+
{ asset: data.asset.baseAsset.name, walletAccount: data.walletAccount },
|
|
93
|
+
txId,
|
|
94
|
+
ALLOWANCE_TX_TIMEOUT
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
return txId
|
|
98
|
+
}
|
|
98
99
|
|
|
99
|
-
export const createApproveSpendingTokens =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const revokeTokenApprovalTxId = await sendApprovalAndWatchConfirmation({
|
|
107
|
-
...data,
|
|
108
|
-
txInput: undefined,
|
|
109
|
-
approveAmount: data.asset.currency.ZERO,
|
|
100
|
+
export const createApproveSpendingTokens =
|
|
101
|
+
({ sendTx, watchTxConfirmation }) =>
|
|
102
|
+
async (data) => {
|
|
103
|
+
const txIds = []
|
|
104
|
+
const sendApprovalAndWatchConfirmation = createSendApprovalAndWatchConfirmation({
|
|
105
|
+
sendTx,
|
|
106
|
+
watchTxConfirmation,
|
|
110
107
|
})
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
108
|
+
if (ZERO_ALLOWANCE_ASSETS.includes(data.asset.name) && !data.approveAmount.isZero) {
|
|
109
|
+
const revokeTokenApprovalTxId = await sendApprovalAndWatchConfirmation({
|
|
110
|
+
...data,
|
|
111
|
+
txInput: undefined,
|
|
112
|
+
approveAmount: data.asset.currency.ZERO,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
txIds.push(revokeTokenApprovalTxId)
|
|
116
|
+
// Hardcoded min gas limit. Otherwise `fetchGasLimit` will fail since `approve(_spender, 0)` is not yet confirmed
|
|
117
|
+
// eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
|
|
118
|
+
data.gasLimit = Math.max(data.gasLimit || 0, APPROVAL_GAS_LIMIT)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const tokenApprovalTxId = await sendApprovalAndWatchConfirmation({ ...data, sendTx })
|
|
122
|
+
txIds.push(tokenApprovalTxId)
|
|
123
|
+
|
|
124
|
+
return txIds
|
|
115
125
|
}
|
|
116
126
|
|
|
117
|
-
|
|
118
|
-
txIds.push(tokenApprovalTxId)
|
|
119
|
-
|
|
120
|
-
return txIds
|
|
121
|
-
}
|
|
127
|
+
export { APPROVAL_GAS_LIMIT, ZERO_ALLOWANCE_ASSETS } from './constants'
|
package/src/ens/addresses.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
const addresses = {
|
|
2
2
|
ethereum: '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e',
|
|
3
3
|
// TODO: add other networks if/when available
|
|
4
4
|
// 'avalanchec': '',
|
|
@@ -6,3 +6,5 @@ export default {
|
|
|
6
6
|
// 'fantommainnet': '',
|
|
7
7
|
// 'matic': '',
|
|
8
8
|
}
|
|
9
|
+
|
|
10
|
+
export default addresses
|
package/src/eth-like-util.js
CHANGED
|
@@ -3,6 +3,7 @@ import { normalizeTxId, isEthereumLikeAsset, isEthereumLikeToken, ABI } from '@e
|
|
|
3
3
|
import { memoizeLruCache } from '@exodus/asset-lib'
|
|
4
4
|
import SolidityContract from '@exodus/solidity-contract'
|
|
5
5
|
import { getServerByName, getServer } from './exodus-eth-server'
|
|
6
|
+
import BN from 'bn.js'
|
|
6
7
|
|
|
7
8
|
export async function isContract(baseAssetName, address) {
|
|
8
9
|
return getServerByName(baseAssetName).isContract(address)
|
|
@@ -41,9 +42,9 @@ export async function getBalance({ asset, address }) {
|
|
|
41
42
|
// Only base assets, not tokens
|
|
42
43
|
export async function getBalanceProxied({ asset, address, tag = 'latest' }) {
|
|
43
44
|
if (!isEthereumLikeAsset(asset)) throw new Error(`unsupported asset ${asset.name}`)
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
return
|
|
45
|
+
const result = await getServer(asset).getBalanceProxied(address)
|
|
46
|
+
const hex = result.startsWith('0x') ? result.slice(2) : result
|
|
47
|
+
return new BN(hex, 'hex').toString()
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
// Only ETH-like assets with token support
|
|
@@ -110,12 +111,12 @@ export const getERC20Params = async ({ asset, address, paramNames = DEFAULT_PARA
|
|
|
110
111
|
callResponse = await server.ethCall({ to: address, data: ERC20[method].methodId })
|
|
111
112
|
} catch (err) {
|
|
112
113
|
if (err.message === 'execution reverted') {
|
|
113
|
-
throw Error(
|
|
114
|
+
throw new Error(
|
|
114
115
|
`Can't find parameters for contract with address ${address}. Are you sure it is a valid ERC20 contract?`
|
|
115
116
|
)
|
|
116
117
|
}
|
|
117
118
|
|
|
118
|
-
throw Error(err.message)
|
|
119
|
+
throw new Error(err.message)
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
if (method === 'decimals') return parseInt(callResponse)
|
package/src/etherscan/account.js
CHANGED
|
@@ -8,7 +8,7 @@ const _request = async (...args) => request(isValidResponseCheck, 'account', ...
|
|
|
8
8
|
export async function fetchBalance(address) {
|
|
9
9
|
const balance = await _request('balance', { address })
|
|
10
10
|
|
|
11
|
-
const isValid =
|
|
11
|
+
const isValid = /^\d+$/.test(balance)
|
|
12
12
|
if (!isValid) throw new RangeError(`Invalid balance: ${balance}`)
|
|
13
13
|
|
|
14
14
|
return balance
|
|
@@ -42,7 +42,7 @@ export async function tokenBalance(token, address) {
|
|
|
42
42
|
}
|
|
43
43
|
const balance = await _request('tokenbalance', params)
|
|
44
44
|
|
|
45
|
-
const isValid =
|
|
45
|
+
const isValid = /^\d+$/.test(balance)
|
|
46
46
|
if (!isValid) throw new RangeError(`Invalid balance: ${balance}`)
|
|
47
47
|
|
|
48
48
|
return balance
|
package/src/etherscan/index.js
CHANGED
|
@@ -1,44 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
fetchBalance as _fetchBalance,
|
|
3
|
-
fetchTxlist as _fetchTxlist,
|
|
4
|
-
fetchTxlistinternal as _fetchTxlistinternal,
|
|
5
|
-
tokenBalance as _tokenBalance,
|
|
6
|
-
} from './account'
|
|
7
|
-
import {
|
|
8
|
-
sendRawTransaction as _sendRawTransaction,
|
|
9
|
-
getTransactionCount as _getTransactionCount,
|
|
10
|
-
getTransactionReceipt as _getTransactionReceipt,
|
|
11
|
-
estimateGas as _estimateGas,
|
|
12
|
-
getCode as _getCode,
|
|
13
|
-
gasPrice as _gasPrice,
|
|
14
|
-
ethCall as _ethCall,
|
|
15
|
-
} from './proxy'
|
|
16
|
-
import { setEtherscanApiKey } from './request'
|
|
17
|
-
import { getLogs as _getLogs } from './logs'
|
|
18
1
|
import createWebSocket from './ws'
|
|
19
2
|
|
|
20
3
|
export const ETHERSCAN_WS_URL = 'wss://socket.etherscan.io/wshandler'
|
|
21
4
|
|
|
22
|
-
export const fetchBalance = _fetchBalance
|
|
23
|
-
export const fetchTxlist = _fetchTxlist
|
|
24
|
-
export const fetchTxlistinternal = _fetchTxlistinternal
|
|
25
|
-
export const tokenBalance = _tokenBalance
|
|
26
|
-
|
|
27
|
-
export const sendRawTransaction = _sendRawTransaction
|
|
28
|
-
export const getTransactionCount = _getTransactionCount
|
|
29
|
-
export const getTransactionReceipt = _getTransactionReceipt
|
|
30
|
-
export const estimateGas = _estimateGas
|
|
31
|
-
export const getCode = _getCode
|
|
32
|
-
export const gasPrice = _gasPrice
|
|
33
|
-
|
|
34
|
-
export const getLogs = _getLogs
|
|
35
|
-
|
|
36
|
-
export const setApiKey = setEtherscanApiKey
|
|
37
|
-
|
|
38
5
|
export const ws = createWebSocket(ETHERSCAN_WS_URL)
|
|
39
6
|
|
|
40
|
-
export const ethCall = _ethCall
|
|
41
|
-
|
|
42
7
|
export function filterTxsSent(addr, etherscanTxs) {
|
|
43
8
|
return etherscanTxs.filter((tx) => tx.from.toLowerCase() === addr.toLowerCase())
|
|
44
9
|
}
|
|
@@ -46,3 +11,18 @@ export function filterTxsSent(addr, etherscanTxs) {
|
|
|
46
11
|
export function filterTxsReceived(addr, etherscanTxs) {
|
|
47
12
|
return etherscanTxs.filter((tx) => tx.to.toLowerCase() === addr.toLowerCase())
|
|
48
13
|
}
|
|
14
|
+
|
|
15
|
+
export { fetchBalance, fetchTxlistinternal, fetchTxlist, tokenBalance } from './account'
|
|
16
|
+
export {
|
|
17
|
+
sendRawTransaction,
|
|
18
|
+
getTransactionCount,
|
|
19
|
+
estimateGas,
|
|
20
|
+
getTransactionReceipt,
|
|
21
|
+
getCode,
|
|
22
|
+
ethCall,
|
|
23
|
+
gasPrice,
|
|
24
|
+
} from './proxy'
|
|
25
|
+
|
|
26
|
+
export { setEtherscanApiKey as setApiKey } from './request'
|
|
27
|
+
|
|
28
|
+
export { getLogs } from './logs'
|
package/src/etherscan/proxy.js
CHANGED
|
@@ -6,7 +6,7 @@ const _request = async (...args) => request(isValidResponseCheck, 'proxy', ...ar
|
|
|
6
6
|
export async function sendRawTransaction(data) {
|
|
7
7
|
const txhash = await _request('eth_sendRawTransaction', { hex: '0x' + data })
|
|
8
8
|
|
|
9
|
-
const isValidTxHash = /^0x[
|
|
9
|
+
const isValidTxHash = /^0x[\dA-Fa-f]{64}$/.test(txhash)
|
|
10
10
|
if (!isValidTxHash) throw new Error(`Invalid tx hash: ${txhash}`)
|
|
11
11
|
|
|
12
12
|
return txhash.slice(2)
|
|
@@ -27,7 +27,7 @@ export async function estimateGas(data) {
|
|
|
27
27
|
export async function getCode(address) {
|
|
28
28
|
const code = await _request('eth_getCode', { address })
|
|
29
29
|
|
|
30
|
-
const isValidCode = /^0x[
|
|
30
|
+
const isValidCode = /^0x[\dA-Fa-f]*$/.test(code) && code.length % 2 === 0
|
|
31
31
|
if (!isValidCode) throw new Error(`Invalid address code: ${code}`)
|
|
32
32
|
|
|
33
33
|
return code
|
|
@@ -36,7 +36,7 @@ export async function getCode(address) {
|
|
|
36
36
|
export async function gasPrice() {
|
|
37
37
|
const price = await _request('eth_gasPrice')
|
|
38
38
|
|
|
39
|
-
const isValidPrice = /^0x[
|
|
39
|
+
const isValidPrice = /^0x[\dA-Fa-f]+$/.test(price)
|
|
40
40
|
if (!isValidPrice) throw new Error(`Invalid price: ${price}`)
|
|
41
41
|
|
|
42
42
|
return price
|
package/src/etherscan/request.js
CHANGED
|
@@ -12,7 +12,7 @@ export function setEtherscanApiKey(apiKey) {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export default makeConcurrent(
|
|
15
|
-
async function(isValidResponseCheck, module, action, params = {}) {
|
|
15
|
+
async function (isValidResponseCheck, module, action, params = {}) {
|
|
16
16
|
const data = await fetchival(new URL(ETHERSCAN_API_URL), { timeout: ms('15s') }).get({
|
|
17
17
|
...params,
|
|
18
18
|
module,
|
package/src/etherscan/ws.js
CHANGED
|
@@ -28,7 +28,7 @@ export default function createWebSocket(url) {
|
|
|
28
28
|
break
|
|
29
29
|
|
|
30
30
|
case 'subscribe-txlist':
|
|
31
|
-
const match = data.message.toLowerCase().match(/0x[
|
|
31
|
+
const match = data.message.toLowerCase().match(/0x[\da-f]{40}/)
|
|
32
32
|
if (match && data.status === '1') events.emit(`address-${match[0]}-subscribed`)
|
|
33
33
|
else ws.close()
|
|
34
34
|
break
|
|
@@ -49,7 +49,7 @@ export default function createWebSocket(url) {
|
|
|
49
49
|
ws.on('message', (data) => {
|
|
50
50
|
try {
|
|
51
51
|
onMessage(data)
|
|
52
|
-
} catch
|
|
52
|
+
} catch {}
|
|
53
53
|
})
|
|
54
54
|
ws.once('open', () => {
|
|
55
55
|
for (const address of addresses.values()) subscribeAddress(address)
|
|
@@ -3,6 +3,7 @@ import { bufferToHex } from '@exodus/ethereumjs-util'
|
|
|
3
3
|
import SolidityContract from '@exodus/solidity-contract'
|
|
4
4
|
import EventEmitter from 'events'
|
|
5
5
|
import { isEmpty } from 'lodash'
|
|
6
|
+
import { fetch } from '@exodus/fetch'
|
|
6
7
|
|
|
7
8
|
export default class ApiCoinNodesServer extends EventEmitter {
|
|
8
9
|
constructor({ uri }) {
|
|
@@ -23,9 +24,7 @@ export default class ApiCoinNodesServer extends EventEmitter {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
const response = await fetch(this.uri, options)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return data
|
|
27
|
+
return response.json()
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
async sendBatchRequest(batch) {
|
|
@@ -37,6 +36,7 @@ export default class ApiCoinNodesServer extends EventEmitter {
|
|
|
37
36
|
if (responses.length !== batch.length || !isValid) {
|
|
38
37
|
throw new Error('Bad rpc batch response')
|
|
39
38
|
}
|
|
39
|
+
|
|
40
40
|
const keyed = responses.reduce((acc, response) => {
|
|
41
41
|
return { ...acc, [`${response.id}`]: response.result }
|
|
42
42
|
}, Object.create(null))
|
|
@@ -52,6 +52,7 @@ export default class ApiCoinNodesServer extends EventEmitter {
|
|
|
52
52
|
const message = error?.message || error?.code || 'no result'
|
|
53
53
|
throw new Error(`Bad rpc response: ${message}`)
|
|
54
54
|
}
|
|
55
|
+
|
|
55
56
|
return result
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -34,7 +34,8 @@ export function create(defaultURL, ensAssetName) {
|
|
|
34
34
|
const data = await err.response.json()
|
|
35
35
|
const msg = data.error.replace(/^RPC error \(code: -\d+\): /, '')
|
|
36
36
|
nerr = new Error(msg)
|
|
37
|
-
} catch
|
|
37
|
+
} catch {}
|
|
38
|
+
|
|
38
39
|
nerr.finalError = true
|
|
39
40
|
}
|
|
40
41
|
|
|
@@ -45,7 +46,7 @@ export function create(defaultURL, ensAssetName) {
|
|
|
45
46
|
// Default retry function
|
|
46
47
|
const requestWithRetry = retry(request, { delayTimesMs: RETRY_DELAYS })
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
return {
|
|
49
50
|
getURL() {
|
|
50
51
|
return API_URL
|
|
51
52
|
},
|
|
@@ -243,13 +244,14 @@ export function create(defaultURL, ensAssetName) {
|
|
|
243
244
|
},
|
|
244
245
|
|
|
245
246
|
async proxyToCoinNode(requestData) {
|
|
247
|
+
// eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
|
|
246
248
|
if (!requestData.jsonrpc) requestData.jsonrpc = '2.0'
|
|
249
|
+
// eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
|
|
247
250
|
if (!requestData.id) requestData.id = randomUUID()
|
|
251
|
+
// eslint-disable-next-line @exodus/mutable/no-param-reassign-prop-only
|
|
248
252
|
if (!requestData.params) requestData.params = []
|
|
249
253
|
|
|
250
254
|
return request('proxy', requestData, { version: 'v2', method: 'post' })
|
|
251
255
|
},
|
|
252
256
|
}
|
|
253
|
-
|
|
254
|
-
return api
|
|
255
257
|
}
|
|
@@ -27,6 +27,7 @@ export default class ClarityServer extends EventEmitter {
|
|
|
27
27
|
this.emit('transaction', { walletAccount, isPending, transaction })
|
|
28
28
|
})
|
|
29
29
|
}
|
|
30
|
+
|
|
30
31
|
return this.sockets[namespace]
|
|
31
32
|
}
|
|
32
33
|
|
|
@@ -35,6 +36,7 @@ export default class ClarityServer extends EventEmitter {
|
|
|
35
36
|
if (!this.sockets[namespace]) {
|
|
36
37
|
this.sockets[namespace] = this.createSocket(namespace)
|
|
37
38
|
}
|
|
39
|
+
|
|
38
40
|
return this.sockets[namespace]
|
|
39
41
|
}
|
|
40
42
|
|
|
@@ -44,6 +46,7 @@ export default class ClarityServer extends EventEmitter {
|
|
|
44
46
|
this.sockets[namespace] = this.createSocket(namespace)
|
|
45
47
|
this.sockets[namespace].on('feeUpdated', (fee) => this.emit('feeUpdated', fee))
|
|
46
48
|
}
|
|
49
|
+
|
|
47
50
|
return this.sockets[namespace]
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -103,9 +106,10 @@ export default class ClarityServer extends EventEmitter {
|
|
|
103
106
|
onChunk(isPending, chunk)
|
|
104
107
|
callback()
|
|
105
108
|
}
|
|
109
|
+
|
|
106
110
|
socket.on('transactionsChunk', listener)
|
|
107
111
|
return new Promise((resolve, reject) => {
|
|
108
|
-
const timeout = setTimeout(() => reject(new Error('Transactions Timeout')),
|
|
112
|
+
const timeout = setTimeout(() => reject(new Error('Transactions Timeout')), 300_000)
|
|
109
113
|
socket.emit('getTransactions', cursor, (nextCursor) => {
|
|
110
114
|
clearTimeout(timeout)
|
|
111
115
|
const uintArray = new Uint8Array(nextCursor)
|
|
@@ -119,13 +123,14 @@ export default class ClarityServer extends EventEmitter {
|
|
|
119
123
|
async getFee() {
|
|
120
124
|
const socket = this.connectFee()
|
|
121
125
|
return new Promise((resolve, reject) => {
|
|
122
|
-
const timeout = setTimeout(() => reject(new Error('Fee Timeout')),
|
|
126
|
+
const timeout = setTimeout(() => reject(new Error('Fee Timeout')), 30_000)
|
|
123
127
|
socket.emit('getFee', (fee) => {
|
|
124
128
|
clearTimeout(timeout)
|
|
125
129
|
if (!fee) {
|
|
126
130
|
const error = new Error('Unable to get fee')
|
|
127
131
|
return reject(error)
|
|
128
132
|
}
|
|
133
|
+
|
|
129
134
|
resolve(fee)
|
|
130
135
|
})
|
|
131
136
|
})
|
|
@@ -140,7 +145,7 @@ export default class ClarityServer extends EventEmitter {
|
|
|
140
145
|
async sendRpcRequest(rpcRequest) {
|
|
141
146
|
const rpcSocket = this.connectRpc()
|
|
142
147
|
return new Promise((resolve, reject) => {
|
|
143
|
-
const timeout = setTimeout(() => reject(new Error('Rpc Timeout')),
|
|
148
|
+
const timeout = setTimeout(() => reject(new Error('Rpc Timeout')), 30_000)
|
|
144
149
|
rpcSocket.emit('request', rpcRequest, (response) => {
|
|
145
150
|
clearTimeout(timeout)
|
|
146
151
|
resolve(response)
|
|
@@ -157,6 +162,7 @@ export default class ClarityServer extends EventEmitter {
|
|
|
157
162
|
if (responses.length !== batch.length || !isValid) {
|
|
158
163
|
throw new Error('Bad Response')
|
|
159
164
|
}
|
|
165
|
+
|
|
160
166
|
const keyed = responses.reduce((acc, response) => {
|
|
161
167
|
return { ...acc, [`${response.id}`]: response.result }
|
|
162
168
|
}, Object.create(null))
|
|
@@ -171,6 +177,7 @@ export default class ClarityServer extends EventEmitter {
|
|
|
171
177
|
const message = error?.message || error?.code || 'no result'
|
|
172
178
|
throw new Error(`Bad rpc response: ${message}`)
|
|
173
179
|
}
|
|
180
|
+
|
|
174
181
|
return result
|
|
175
182
|
}
|
|
176
183
|
|
|
@@ -48,12 +48,16 @@ export default function createWebSocket(getURL) {
|
|
|
48
48
|
if (ws) return
|
|
49
49
|
|
|
50
50
|
ws = new WebSocket(getURL())
|
|
51
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
51
52
|
ws.onerror = (e) => {}
|
|
53
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
52
54
|
ws.onmessage = (e) => {
|
|
53
55
|
try {
|
|
54
56
|
onMessage(e.data)
|
|
55
|
-
} catch
|
|
57
|
+
} catch {}
|
|
56
58
|
}
|
|
59
|
+
|
|
60
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
57
61
|
ws.onopen = () => {
|
|
58
62
|
for (const address of addresses.values()) subscribeAddress(address)
|
|
59
63
|
subscribeGasPrice()
|
|
@@ -62,9 +66,12 @@ export default function createWebSocket(getURL) {
|
|
|
62
66
|
} else {
|
|
63
67
|
console.warn('Client side ping not available')
|
|
64
68
|
}
|
|
69
|
+
|
|
65
70
|
isWSOpened = true
|
|
66
71
|
events.emit('open')
|
|
67
72
|
}
|
|
73
|
+
|
|
74
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
68
75
|
ws.onclose = () => {
|
|
69
76
|
ws = null
|
|
70
77
|
isWSOpened = false
|
|
@@ -72,6 +79,7 @@ export default function createWebSocket(getURL) {
|
|
|
72
79
|
if (opened) {
|
|
73
80
|
openTimeoutId = setTimeout(open, reconnectDelay())
|
|
74
81
|
}
|
|
82
|
+
|
|
75
83
|
events.emit('close')
|
|
76
84
|
}
|
|
77
85
|
}
|
|
@@ -81,9 +89,13 @@ export default function createWebSocket(getURL) {
|
|
|
81
89
|
isWSOpened = false
|
|
82
90
|
clearTimeout(openTimeoutId)
|
|
83
91
|
if (!ws) return
|
|
92
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
84
93
|
ws.onerror = null
|
|
94
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
85
95
|
ws.onmessage = null
|
|
96
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
86
97
|
ws.onopen = null
|
|
98
|
+
// eslint-disable-next-line unicorn/prefer-add-event-listener
|
|
87
99
|
ws.onclose = null
|
|
88
100
|
try {
|
|
89
101
|
ws.close()
|
|
@@ -9,7 +9,10 @@ export class EthereumArbnovaFeeMonitor extends EthereumLikeFeeMonitor {
|
|
|
9
9
|
super({
|
|
10
10
|
updateFee,
|
|
11
11
|
assetName,
|
|
12
|
-
getGasPrice: async () =>
|
|
12
|
+
getGasPrice: async () => {
|
|
13
|
+
const fee = await getServerByName(assetName).getFee()
|
|
14
|
+
return fee?.gasPrice
|
|
15
|
+
},
|
|
13
16
|
interval,
|
|
14
17
|
})
|
|
15
18
|
}
|
|
@@ -9,7 +9,10 @@ export class EthereumArboneFeeMonitor extends EthereumLikeFeeMonitor {
|
|
|
9
9
|
super({
|
|
10
10
|
updateFee,
|
|
11
11
|
assetName,
|
|
12
|
-
getGasPrice: async () =>
|
|
12
|
+
getGasPrice: async () => {
|
|
13
|
+
const fee = await getServerByName(assetName).getFee()
|
|
14
|
+
return fee?.gasPrice
|
|
15
|
+
},
|
|
13
16
|
interval,
|
|
14
17
|
})
|
|
15
18
|
}
|