@exodus/ethereum-api 8.7.0 → 8.8.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 +32 -0
- package/package.json +2 -2
- package/src/error-wrapper.js +89 -0
- package/src/eth-like-util.js +27 -7
- package/src/exodus-eth-server/api-coin-nodes.js +1 -0
- package/src/exodus-eth-server/clarity.js +1 -0
- package/src/gas-estimation.js +11 -10
- package/src/get-fee-async.js +4 -4
- package/src/index.js +1 -0
- package/src/nft-utils.js +6 -1
- package/src/tx-log/clarity-monitor.js +4 -2
- package/src/tx-log/ethereum-monitor.js +7 -4
- package/src/tx-log/ethereum-no-history-monitor.js +5 -9
- package/src/tx-send/tx-send.js +40 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
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.8.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.7.1...@exodus/ethereum-api@8.8.0) (2024-07-02)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* Attach some error reasons to create tx ([#2649](https://github.com/ExodusMovement/assets/issues/2649)) ([0f783e2](https://github.com/ExodusMovement/assets/commit/0f783e2a40079ce407b366e0f6411d279c1fb0ce)), closes [#2646](https://github.com/ExodusMovement/assets/issues/2646)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* ethereum-api default url in servers ([#2689](https://github.com/ExodusMovement/assets/issues/2689)) ([2fc70a4](https://github.com/ExodusMovement/assets/commit/2fc70a49c05f18f6153009b1cebdd9ea522509ac))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## [8.7.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.7.0...@exodus/ethereum-api@8.7.1) (2024-06-25)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* get fee async txInput ([#2665](https://github.com/ExodusMovement/assets/issues/2665)) ([20f9358](https://github.com/ExodusMovement/assets/commit/20f93587ade7291aff6a44872cbaead93b80d1f3))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
6
29
|
## [8.7.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.6.0...@exodus/ethereum-api@8.7.0) (2024-06-21)
|
|
7
30
|
|
|
8
31
|
|
|
@@ -45,6 +68,15 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline
|
|
|
45
68
|
|
|
46
69
|
|
|
47
70
|
|
|
71
|
+
## 8.4.1-exodus.0
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
### Bug Fixes
|
|
75
|
+
|
|
76
|
+
* send fromAddress when approving tokens ([#2600](https://github.com/ExodusMovement/assets/pull/2600) )
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
48
80
|
## [8.4.1](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.4.0...@exodus/ethereum-api@8.4.1) (2024-06-13)
|
|
49
81
|
|
|
50
82
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.8.0",
|
|
4
4
|
"description": "Ethereum Api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"type": "git",
|
|
67
67
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "2429beb0c513a4e0da8de7e4c070f0774e5f62c4"
|
|
70
70
|
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export const reasons = {
|
|
2
|
+
fetchGasLimitFailed: 'Fetch gas limit failed',
|
|
3
|
+
nonceFetchFailed: 'Nonce fetch failed',
|
|
4
|
+
balanceFetchFailed: 'Balance fetch failed',
|
|
5
|
+
broadcastTxFailed: 'Broadcast tx failed',
|
|
6
|
+
getTransactionByHashFailed: 'Get transaction by hash failed',
|
|
7
|
+
ethCallErc20Failed: 'Eth call erc20 failed',
|
|
8
|
+
insufficientFunds: 'Insufficient funds',
|
|
9
|
+
bumpTxFailed: 'Bump tx failed',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const MAX_HINT_LENGTH = 100
|
|
13
|
+
// TODO: move this to be an generic error for all assets
|
|
14
|
+
export class EthLikeError extends Error {
|
|
15
|
+
#hintStack
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Creates an instance of EthLikeError.
|
|
19
|
+
*
|
|
20
|
+
* @param {string} message - Standard error message.
|
|
21
|
+
* @param {string} tag - Tag associated with the error.
|
|
22
|
+
* @param {string} reason - A constant indicating the generic failure. Must not contain any sensitive information such as private keys, transaction IDs, or wallet addresses.
|
|
23
|
+
* @param {string} hint - A hint to help the user understand the error. Must not contain any sensitive information such as private keys, transaction IDs, or wallet addresses.
|
|
24
|
+
*/
|
|
25
|
+
constructor({ message, reason, hint }) {
|
|
26
|
+
super(message)
|
|
27
|
+
this.name = 'EthLikeError'
|
|
28
|
+
this.#hintStack = [hint] // NOTE: we can add more hints to the stack
|
|
29
|
+
this.reason = reason
|
|
30
|
+
this.hint = this.#extractHint(hint)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
addHint = (hint) => {
|
|
34
|
+
const filteredHint = this.#extractHint(hint)
|
|
35
|
+
if (!filteredHint) {
|
|
36
|
+
return this
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.#hintStack.push(hint)
|
|
40
|
+
this.hint = `${this.#hintStack.join(':')}`
|
|
41
|
+
return this
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// NOTE: a class method for now, if we move this to be a generic error for all assets, each asset will have its own implementation
|
|
45
|
+
#extractHint = (hint) => {
|
|
46
|
+
if (!hint) return ''
|
|
47
|
+
|
|
48
|
+
// Define regex patterns for sensitive information
|
|
49
|
+
const sensitivePatterns = [
|
|
50
|
+
/(?:0x)?[\dA-Fa-f]{64}/g, // Pattern for transaction hashes (hex string of 64 characters, optional 0x prefix)
|
|
51
|
+
/(?:0x)?[\dA-Fa-f]{40}/g, // Pattern for wallet addresses (hex string of 40 characters, optional 0x prefix)
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
let filteredHint = hint
|
|
55
|
+
|
|
56
|
+
// Check for sensitive information
|
|
57
|
+
const containsSensitiveInfo = sensitivePatterns.some((pattern) => pattern.test(filteredHint))
|
|
58
|
+
|
|
59
|
+
if (containsSensitiveInfo) {
|
|
60
|
+
console.warn('Hint contains sensitive information and will be ignored.')
|
|
61
|
+
return ''
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Limit length to 100 characters
|
|
65
|
+
if (filteredHint.length > MAX_HINT_LENGTH) {
|
|
66
|
+
filteredHint = filteredHint.slice(0, MAX_HINT_LENGTH - 3) + '...'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return filteredHint
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export const withErrorReason = async (promise, errorReason, hint) => {
|
|
74
|
+
try {
|
|
75
|
+
return await promise
|
|
76
|
+
} catch (err) {
|
|
77
|
+
if (err instanceof EthLikeError) {
|
|
78
|
+
// ignore errorReason and add hint to stack
|
|
79
|
+
err.addHint(hint)
|
|
80
|
+
throw err
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
throw new EthLikeError({
|
|
84
|
+
message: err.message,
|
|
85
|
+
reason: errorReason,
|
|
86
|
+
hint,
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
}
|
package/src/eth-like-util.js
CHANGED
|
@@ -4,6 +4,7 @@ import { memoizeLruCache } from '@exodus/asset-lib'
|
|
|
4
4
|
import SolidityContract from '@exodus/solidity-contract'
|
|
5
5
|
import { getServerByName, getServer } from './exodus-eth-server'
|
|
6
6
|
import { fromHexToString } from './number-utils'
|
|
7
|
+
import * as ErrorWrapper from './error-wrapper'
|
|
7
8
|
|
|
8
9
|
export async function isContract(baseAssetName, address) {
|
|
9
10
|
return getServerByName(baseAssetName).isContract(address)
|
|
@@ -35,17 +36,27 @@ export const isForwarderContractCached = memoizeLruCache(
|
|
|
35
36
|
|
|
36
37
|
export async function getNonce({ asset, address, tag = 'latest' }) {
|
|
37
38
|
const server = getServer(asset)
|
|
38
|
-
const nonce = await
|
|
39
|
+
const nonce = await ErrorWrapper.withErrorReason(
|
|
40
|
+
server.getTransactionCount(address, tag),
|
|
41
|
+
ErrorWrapper.reasons.nonceFetchFailed,
|
|
42
|
+
'getNonce'
|
|
43
|
+
)
|
|
44
|
+
|
|
39
45
|
return parseInt(nonce, 16)
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
export async function estimateGas({ asset, ...args }) {
|
|
43
|
-
return
|
|
49
|
+
return ErrorWrapper.withErrorReason(
|
|
50
|
+
getServer(asset).estimateGas(args),
|
|
51
|
+
ErrorWrapper.reasons.fetchGasLimitFailed,
|
|
52
|
+
'estimateGas'
|
|
53
|
+
)
|
|
44
54
|
}
|
|
45
55
|
|
|
46
56
|
// Only base assets, not tokens
|
|
47
57
|
export async function getBalance({ asset, address }) {
|
|
48
58
|
if (!isEthereumLikeAsset(asset)) throw new Error(`unsupported asset ${asset.name}`)
|
|
59
|
+
|
|
49
60
|
const server = getServer(asset)
|
|
50
61
|
const balances = await server.getBalance(address)
|
|
51
62
|
return balances?.confirmed?.value || '0'
|
|
@@ -54,6 +65,7 @@ export async function getBalance({ asset, address }) {
|
|
|
54
65
|
// Only base assets, not tokens
|
|
55
66
|
export async function getBalanceProxied({ asset, address, tag = 'latest' }) {
|
|
56
67
|
if (!isEthereumLikeAsset(asset)) throw new Error(`unsupported asset ${asset.name}`)
|
|
68
|
+
|
|
57
69
|
const result = await getServer(asset).getBalanceProxied(address)
|
|
58
70
|
return fromHexToString(result)
|
|
59
71
|
}
|
|
@@ -61,6 +73,7 @@ export async function getBalanceProxied({ asset, address, tag = 'latest' }) {
|
|
|
61
73
|
// Only ETH-like assets with token support
|
|
62
74
|
export async function getTokenBalance({ asset, address }) {
|
|
63
75
|
if (!isEthereumLikeToken(asset)) throw new Error(`unsupported ETH-like token ${asset.name}`)
|
|
76
|
+
|
|
64
77
|
const server = getServer(asset)
|
|
65
78
|
const balances = await server.getBalance(address)
|
|
66
79
|
const contractAddress = asset.contract.address.toLowerCase()
|
|
@@ -69,6 +82,7 @@ export async function getTokenBalance({ asset, address }) {
|
|
|
69
82
|
|
|
70
83
|
export async function getTokenBalanceFromNode({ asset, address }) {
|
|
71
84
|
if (!isEthereumLikeToken(asset)) throw new Error(`unsupported ETH-like token ${asset.name}`)
|
|
85
|
+
|
|
72
86
|
const server = getServer(asset)
|
|
73
87
|
const contractAddress = asset.contract.address.toLowerCase()
|
|
74
88
|
const balances = await server.balanceOf(address, contractAddress)
|
|
@@ -83,7 +97,7 @@ export function sendRawTransaction(asset) {
|
|
|
83
97
|
export async function transactionExists({ asset, txId }) {
|
|
84
98
|
const server = getServer(asset)
|
|
85
99
|
txId = normalizeTxId(txId)
|
|
86
|
-
const txResult =
|
|
100
|
+
const txResult = server.getTransactionByHash(txId)
|
|
87
101
|
return txResult && txResult.hash === txId
|
|
88
102
|
}
|
|
89
103
|
|
|
@@ -122,12 +136,18 @@ export const getERC20Params = async ({ asset, address, paramNames = DEFAULT_PARA
|
|
|
122
136
|
callResponse = await server.ethCall({ to: address, data: ERC20[method].methodId })
|
|
123
137
|
} catch (err) {
|
|
124
138
|
if (err.message === 'execution reverted') {
|
|
125
|
-
throw new
|
|
126
|
-
`Can't find parameters for contract with address ${address}. Are you sure it is a valid ERC20 contract
|
|
127
|
-
|
|
139
|
+
throw new ErrorWrapper.EthLikeError({
|
|
140
|
+
message: `Can't find parameters for contract with address ${address}. Are you sure it is a valid ERC20 contract?`,
|
|
141
|
+
reason: ErrorWrapper.reasons.ethCallErc20Failed,
|
|
142
|
+
hint: 'ethCall:executionReverted',
|
|
143
|
+
})
|
|
128
144
|
}
|
|
129
145
|
|
|
130
|
-
throw new
|
|
146
|
+
throw new ErrorWrapper.EthLikeError({
|
|
147
|
+
message: err.message,
|
|
148
|
+
reason: ErrorWrapper.reasons.ethCallErc20Failed,
|
|
149
|
+
hint: 'ethCall',
|
|
150
|
+
})
|
|
131
151
|
}
|
|
132
152
|
|
|
133
153
|
if (method === 'decimals') return parseInt(callResponse)
|
package/src/gas-estimation.js
CHANGED
|
@@ -38,6 +38,14 @@ export async function estimateGasLimit(
|
|
|
38
38
|
.toNumber()
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
export function resolveDefaultTxInput({ asset, toAddress, amount }) {
|
|
42
|
+
return isEthereumLikeToken(asset)
|
|
43
|
+
? ethUtil.bufferToHex(
|
|
44
|
+
asset.contract.transfer.build(toAddress.toLowerCase(), amount.toBaseString())
|
|
45
|
+
)
|
|
46
|
+
: '0x'
|
|
47
|
+
}
|
|
48
|
+
|
|
41
49
|
export async function fetchGasLimit({
|
|
42
50
|
asset,
|
|
43
51
|
fromAddress,
|
|
@@ -53,22 +61,14 @@ export async function fetchGasLimit({
|
|
|
53
61
|
|
|
54
62
|
if (!amount) amount = asset.currency.ZERO
|
|
55
63
|
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
const txInput =
|
|
59
|
-
providedTxInput ||
|
|
60
|
-
(isToken
|
|
61
|
-
? ethUtil.bufferToHex(
|
|
62
|
-
asset.contract.transfer.build(toAddress.toLowerCase(), amount.toBaseString())
|
|
63
|
-
)
|
|
64
|
-
: '0x')
|
|
64
|
+
const txInput = providedTxInput || resolveDefaultTxInput({ asset, toAddress, amount })
|
|
65
65
|
|
|
66
66
|
const defaultGasLimit = () =>
|
|
67
67
|
asset.gasLimit + GAS_PER_NON_ZERO_BYTE * ethUtil.toBuffer(txInput).length
|
|
68
68
|
|
|
69
69
|
const isContract = await isContractAddressCached({ asset, address: toAddress })
|
|
70
70
|
|
|
71
|
-
if (
|
|
71
|
+
if (isEthereumLikeToken(asset)) {
|
|
72
72
|
amount = asset.baseAsset.currency.ZERO
|
|
73
73
|
toAddress = asset.contract.address
|
|
74
74
|
} else if (
|
|
@@ -102,6 +102,7 @@ export async function fetchGasLimit({
|
|
|
102
102
|
)
|
|
103
103
|
} catch (err) {
|
|
104
104
|
if (throwOnError) throw err
|
|
105
|
+
|
|
105
106
|
console.error('fetchGasLimit error', err)
|
|
106
107
|
|
|
107
108
|
// fallback value for contract case
|
package/src/get-fee-async.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import assert from 'minimalistic-assert'
|
|
2
|
-
import { fetchGasLimit } from './gas-estimation'
|
|
2
|
+
import { fetchGasLimit, resolveDefaultTxInput } from './gas-estimation'
|
|
3
3
|
import { isForwarderContractCached } from './eth-like-util'
|
|
4
4
|
import { getFeeFactory } from './get-fee'
|
|
5
5
|
import { getNftArguments } from './nft-utils'
|
|
@@ -52,13 +52,14 @@ const getFeeAsyncFactory = ({
|
|
|
52
52
|
return getNftArguments({ asset, nft, fromAddress, toAddress })
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
if (!amount) amount = asset.currency.ZERO
|
|
55
56
|
const extraPercentage = await resolveExtraPercentage({ asset, toAddress })
|
|
56
|
-
const txInput = txInputPram
|
|
57
|
+
const txInput = txInputPram || resolveDefaultTxInput({ asset, toAddress, amount })
|
|
57
58
|
const gasLimit = await fetchGasLimit({
|
|
58
59
|
asset,
|
|
59
60
|
fromAddress,
|
|
60
61
|
toAddress,
|
|
61
|
-
txInput,
|
|
62
|
+
txInput,
|
|
62
63
|
amount,
|
|
63
64
|
bip70,
|
|
64
65
|
extraPercentage,
|
|
@@ -104,7 +105,6 @@ const getFeeAsyncFactory = ({
|
|
|
104
105
|
fee: fee.add(l1DataFee),
|
|
105
106
|
optimismL1DataFee,
|
|
106
107
|
gasLimit,
|
|
107
|
-
txInput,
|
|
108
108
|
...rest,
|
|
109
109
|
}
|
|
110
110
|
}
|
package/src/index.js
CHANGED
|
@@ -11,5 +11,6 @@ export * from './simulate-tx'
|
|
|
11
11
|
export * from './allowance'
|
|
12
12
|
export * from './optimism-gas'
|
|
13
13
|
export * from './number-utils'
|
|
14
|
+
export { reasons as errorReasons, withErrorReason, EthLikeError } from './error-wrapper'
|
|
14
15
|
export { txSendFactory, getFeeInfo } from './tx-send'
|
|
15
16
|
export { createAssetFactory } from './create-asset'
|
package/src/nft-utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import SolidityContract from '@exodus/solidity-contract'
|
|
2
2
|
import { fetchGasLimit } from './gas-estimation'
|
|
3
3
|
import assert from 'minimalistic-assert'
|
|
4
|
+
import * as ErrorWrapper from './error-wrapper'
|
|
4
5
|
|
|
5
6
|
export const getNftArguments = async ({ asset, nft, fromAddress, toAddress }) => {
|
|
6
7
|
assert(asset, 'asset is required')
|
|
@@ -43,7 +44,11 @@ export const getNftArguments = async ({ asset, nft, fromAddress, toAddress }) =>
|
|
|
43
44
|
}
|
|
44
45
|
})
|
|
45
46
|
).catch((e) => {
|
|
46
|
-
throw new
|
|
47
|
+
throw new ErrorWrapper.EthLikeError({
|
|
48
|
+
message: errors.join('\n'),
|
|
49
|
+
reason: ErrorWrapper.reasons.fetchGasLimitFailed,
|
|
50
|
+
hint: 'getNftArguments',
|
|
51
|
+
})
|
|
47
52
|
})
|
|
48
53
|
return {
|
|
49
54
|
contractAddress,
|
|
@@ -26,11 +26,13 @@ export class ClarityMonitor extends BaseMonitor {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
setServer(config) {
|
|
29
|
-
|
|
29
|
+
const uri = config?.server || this.server.defaultUri
|
|
30
|
+
|
|
31
|
+
if (uri === this.server.uri) {
|
|
30
32
|
return
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
this.server.setURI(
|
|
35
|
+
this.server.setURI(uri)
|
|
34
36
|
this.subscribeWalletAddresses()
|
|
35
37
|
if (this.config.GAS_PRICE_FROM_WEBSOCKET) {
|
|
36
38
|
this.server.connectFee()
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { getServer } from '../exodus-eth-server'
|
|
2
1
|
import { isRpcBalanceAsset, getAssetAddresses } from '@exodus/ethereum-lib'
|
|
3
2
|
|
|
4
3
|
import {
|
|
@@ -28,7 +27,7 @@ import { fromHexToString } from '../number-utils'
|
|
|
28
27
|
export class EthereumMonitor extends BaseMonitor {
|
|
29
28
|
constructor({ server, config, ...args }) {
|
|
30
29
|
super(args)
|
|
31
|
-
this.server = server
|
|
30
|
+
this.server = server
|
|
32
31
|
this.config = { GAS_PRICE_FROM_WEBSOCKET: true, ...config }
|
|
33
32
|
this.deriveDataNeededForTick = getDeriveDataNeededForTick(this.aci)
|
|
34
33
|
this.deriveTransactionsToCheck = getDeriveTransactionsToCheck({
|
|
@@ -41,9 +40,13 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
setServer(config = {}) {
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
const uri = config?.serverv1 || this.server.defaultUri
|
|
44
|
+
|
|
45
|
+
if (uri === this.server.getURL()) {
|
|
46
|
+
return
|
|
46
47
|
}
|
|
48
|
+
|
|
49
|
+
this.server.setURL(uri)
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
async deriveData({ assetSource, tokens }) {
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
import { getServer } from '../exodus-eth-server'
|
|
2
|
-
// eslint-disable-next-line import/no-deprecated
|
|
3
|
-
import { DEFAULT_SERVER_URLS } from '@exodus/ethereum-lib'
|
|
4
1
|
import { Tx } from '@exodus/models'
|
|
5
2
|
|
|
6
3
|
import {
|
|
@@ -22,7 +19,7 @@ import { SynchronizedTime } from '@exodus/basic-utils'
|
|
|
22
19
|
export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
23
20
|
constructor({ server, config, ...args }) {
|
|
24
21
|
super(args)
|
|
25
|
-
this.server = server
|
|
22
|
+
this.server = server
|
|
26
23
|
this.config = { ...config }
|
|
27
24
|
this.deriveDataNeededForTick = getDeriveDataNeededForTick(this.aci)
|
|
28
25
|
this.deriveTransactionsToCheck = getDeriveTransactionsToCheck({
|
|
@@ -31,14 +28,13 @@ export class EthereumNoHistoryMonitor extends BaseMonitor {
|
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
setServer(config) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
const uri = config?.server || this.server.defaultUri
|
|
32
|
+
|
|
33
|
+
if (uri === this.server.uri) {
|
|
37
34
|
return
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
this.server.setURI(config.server)
|
|
37
|
+
this.server.setURI(uri)
|
|
42
38
|
}
|
|
43
39
|
|
|
44
40
|
async getBalances({ tokens, ourWalletAddress }) {
|
package/src/tx-send/tx-send.js
CHANGED
|
@@ -5,6 +5,7 @@ import { calculateBumpedGasPrice, isEthereumLikeToken, normalizeTxId } from '@ex
|
|
|
5
5
|
import assert from 'minimalistic-assert'
|
|
6
6
|
import getFeeInfo from './get-fee-info'
|
|
7
7
|
import { getNftArguments } from '../nft-utils'
|
|
8
|
+
import * as ErrorWrapper from '../error-wrapper'
|
|
8
9
|
|
|
9
10
|
const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
|
|
10
11
|
assert(assetClientInterface, 'assetClientInterface is required')
|
|
@@ -127,20 +128,56 @@ const txSendFactory = ({ assetClientInterface, createUnsignedTx }) => {
|
|
|
127
128
|
await baseAsset.api.broadcastTx(rawTx.toString('hex'))
|
|
128
129
|
} catch (err) {
|
|
129
130
|
const nonceTooLowErr = err.message.match(/nonce (is |)too low/i)
|
|
131
|
+
const insufficientFundsErr = err.message.match(/insufficient funds/i)
|
|
130
132
|
const txAlreadyExists = nonceTooLowErr
|
|
131
133
|
? await transactionExists({ asset, txId })
|
|
132
134
|
: err.message.match(/already known/i)
|
|
133
135
|
|
|
134
136
|
if (txAlreadyExists) {
|
|
135
137
|
console.info('tx already broadcast') // inject logger factory from platform
|
|
136
|
-
} else if (
|
|
137
|
-
throw
|
|
138
|
+
} else if (insufficientFundsErr) {
|
|
139
|
+
throw new ErrorWrapper.EthLikeError({
|
|
140
|
+
message: err.message,
|
|
141
|
+
reason: ErrorWrapper.reasons.insufficientFunds,
|
|
142
|
+
hint: 'broadcastTx',
|
|
143
|
+
})
|
|
144
|
+
} else if (bumpTxId) {
|
|
145
|
+
throw new ErrorWrapper.EthLikeError({
|
|
146
|
+
message: err.message,
|
|
147
|
+
reason: ErrorWrapper.reasons.bumpTxFailed,
|
|
148
|
+
hint: 'broadcastTx',
|
|
149
|
+
})
|
|
150
|
+
} else if (!nonceTooLowErr) {
|
|
151
|
+
throw new ErrorWrapper.EthLikeError({
|
|
152
|
+
message: err.message,
|
|
153
|
+
reason: ErrorWrapper.reasons.broadcastTxFailed,
|
|
154
|
+
hint: 'otherErr:broadcastTx',
|
|
155
|
+
})
|
|
138
156
|
} else if (nonceTooLowErr) {
|
|
139
157
|
console.info('trying to send again...') // inject logger factory from platform
|
|
140
158
|
// let's try to fix the nonce issue
|
|
159
|
+
|
|
141
160
|
nonce = await getNonce({ asset: baseAsset, address: fromAddress })
|
|
142
161
|
;({ txId, rawTx } = await createTx({ ...createTxParams, nonce }))
|
|
143
|
-
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
await baseAsset.api.broadcastTx(rawTx.toString('hex'))
|
|
165
|
+
} catch (err) {
|
|
166
|
+
const insufficientFundsErr = err.message.match(/insufficient funds/i)
|
|
167
|
+
if (insufficientFundsErr) {
|
|
168
|
+
throw new ErrorWrapper.EthLikeError({
|
|
169
|
+
message: err.message,
|
|
170
|
+
reason: ErrorWrapper.reasons.insufficientFunds,
|
|
171
|
+
hint: 'retry:broadcastTx',
|
|
172
|
+
})
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
throw new ErrorWrapper.EthLikeError({
|
|
176
|
+
message: err.message,
|
|
177
|
+
reason: ErrorWrapper.reasons.broadcastTxFailed,
|
|
178
|
+
hint: 'retry:broadcastTx',
|
|
179
|
+
})
|
|
180
|
+
}
|
|
144
181
|
}
|
|
145
182
|
}
|
|
146
183
|
|