@exodus/ethereum-api 8.8.0 → 8.9.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 +14 -0
- package/package.json +3 -3
- package/src/create-asset-plugin-factory.js +162 -0
- package/src/create-asset.js +7 -2
- package/src/create-token-factory.js +3 -3
- package/src/get-balances.js +34 -28
- package/src/index.js +4 -0
- package/src/tx-log/ethereum-monitor.js +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,20 @@
|
|
|
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.9.0](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.8.0...@exodus/ethereum-api@8.9.0) (2024-07-03)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* dynamic evm factory ([#2659](https://github.com/ExodusMovement/assets/issues/2659)) ([0baae82](https://github.com/ExodusMovement/assets/commit/0baae82a7f53c8808c9cd2621cf04e9960568cc6))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* **ethereum:** false dropped tx check ([#2707](https://github.com/ExodusMovement/assets/issues/2707)) ([d05a8c8](https://github.com/ExodusMovement/assets/commit/d05a8c895563fb13120956c0081db7ebe86195d6))
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
6
20
|
## [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
21
|
|
|
8
22
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.9.0",
|
|
4
4
|
"description": "Ethereum Api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"lint:fix": "yarn lint --fix"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
+
"@exodus/asset": "^1.2.0",
|
|
23
24
|
"@exodus/asset-lib": "^4.2.0",
|
|
24
25
|
"@exodus/assets": "^9.1.1",
|
|
25
26
|
"@exodus/basic-utils": "^2.1.0",
|
|
@@ -48,7 +49,6 @@
|
|
|
48
49
|
"ws": "^6.1.0"
|
|
49
50
|
},
|
|
50
51
|
"devDependencies": {
|
|
51
|
-
"@exodus/assets": "^9.1.1",
|
|
52
52
|
"@exodus/assets-testing": "^1.0.0",
|
|
53
53
|
"@exodus/bsc-meta": "^1.2.2",
|
|
54
54
|
"@exodus/ethereumarbone-meta": "^1.2.0",
|
|
@@ -66,5 +66,5 @@
|
|
|
66
66
|
"type": "git",
|
|
67
67
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
68
68
|
},
|
|
69
|
-
"gitHead": "
|
|
69
|
+
"gitHead": "237c799e9bc8df6574be95056a999896e2503290"
|
|
70
70
|
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { createMetaDef } from '@exodus/asset'
|
|
2
|
+
import { FeeData } from '@exodus/asset-lib'
|
|
3
|
+
import { createAssetFactory } from './create-asset'
|
|
4
|
+
import assert from 'minimalistic-assert'
|
|
5
|
+
import { UnitType } from '@exodus/currency'
|
|
6
|
+
|
|
7
|
+
// Move to ethereum-api for config base evm
|
|
8
|
+
export const createAssetPluginFactory = (config) => {
|
|
9
|
+
assert(config, 'config is required')
|
|
10
|
+
assert(config.chainId, 'chainId is required')
|
|
11
|
+
assert(config.meta, 'config.meta is required')
|
|
12
|
+
assert(config.meta.explorerUrl, 'meta.explorerUrl is required')
|
|
13
|
+
|
|
14
|
+
const chainId = config.chainId
|
|
15
|
+
const { explorerUrl, ...meta } = config.meta
|
|
16
|
+
const name = `evm_${chainId}`
|
|
17
|
+
const ticker = `EVM${chainId}`
|
|
18
|
+
const tokenType = `EVM_${chainId}_TOKEN`
|
|
19
|
+
|
|
20
|
+
const units = {
|
|
21
|
+
wei: 0,
|
|
22
|
+
Kwei: 3,
|
|
23
|
+
Mwei: 6,
|
|
24
|
+
Gwei: 9,
|
|
25
|
+
[ticker]: config.decimals || 18,
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const currency = UnitType.create(units)
|
|
29
|
+
|
|
30
|
+
// adds lots of validation in config
|
|
31
|
+
const tokenOverrides = (token) => ({
|
|
32
|
+
...token,
|
|
33
|
+
assetType: tokenType,
|
|
34
|
+
contract: token.addresses,
|
|
35
|
+
gasLimit: meta.gasLimit || 120e3,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const blockExplorer = {
|
|
39
|
+
addressUrl: (address) => `${explorerUrl}/address/${encodeURIComponent(address)}`,
|
|
40
|
+
txUrl: (txId) => `${explorerUrl}/tx/${encodeURIComponent(txId)}`,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const assetType = 'ETHEREUM_LIKE'
|
|
44
|
+
const { asset, assetsList } = createMetaDef({
|
|
45
|
+
assetParams: {
|
|
46
|
+
...meta,
|
|
47
|
+
name,
|
|
48
|
+
ticker,
|
|
49
|
+
units,
|
|
50
|
+
chainId,
|
|
51
|
+
assetType,
|
|
52
|
+
blockExplorer,
|
|
53
|
+
},
|
|
54
|
+
tokensParams: meta.tokens || [],
|
|
55
|
+
tokenOverrides,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const resolveFeeDataConfigDefaults = () => {
|
|
59
|
+
const shared = {
|
|
60
|
+
tipGasPrice: '0 Gwei',
|
|
61
|
+
fuelThreshold: '0 Gwei',
|
|
62
|
+
gasPriceEconomicalRate: 0.8,
|
|
63
|
+
gasPriceMinimumRate: 0.6,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (config.eip1559Enabled === true) {
|
|
67
|
+
assert(config.baseFeePerGas, 'baseFeePerGas is required')
|
|
68
|
+
const baseFeePerGas = currency.parse(config.baseFeePerGas)
|
|
69
|
+
const gasPrice = config.gasPrice ? currency.parse(config.gasPrice) : baseFeePerGas.mul(1.5)
|
|
70
|
+
return {
|
|
71
|
+
gasPrice: gasPrice.toBaseString({ unit: true }),
|
|
72
|
+
baseFeePerGas: baseFeePerGas.toBaseString({ unit: true }),
|
|
73
|
+
max: gasPrice.mul(5).toBaseString({ unit: true }),
|
|
74
|
+
min: gasPrice.div(5).toBaseString({ unit: true }),
|
|
75
|
+
eip1559Enabled: config.eip1559Enabled,
|
|
76
|
+
...shared,
|
|
77
|
+
...config.feeData,
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (config.eip1559Enabled === false) {
|
|
82
|
+
assert(config.gasPrice, 'gasPrice is required')
|
|
83
|
+
const gasPrice = currency.parse(config.gasPrice)
|
|
84
|
+
return {
|
|
85
|
+
gasPrice: gasPrice.toBaseString({ unit: true }),
|
|
86
|
+
max: gasPrice.mul(5).toBaseString({ unit: true }),
|
|
87
|
+
min: gasPrice.div(5).toBaseString({ unit: true }),
|
|
88
|
+
eip1559Enabled: config.eip1559Enabled,
|
|
89
|
+
...shared,
|
|
90
|
+
...config.feeData,
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
gasPrice: '0.02 Gwei',
|
|
96
|
+
baseFeePerGas: '0.02 Gwei',
|
|
97
|
+
eip1559Enabled: true,
|
|
98
|
+
...shared,
|
|
99
|
+
...config.feeData,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const feeData = new FeeData({
|
|
104
|
+
config: resolveFeeDataConfigDefaults(),
|
|
105
|
+
mainKey: 'gasPrice',
|
|
106
|
+
currency: asset.currency,
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const createAsset = createAssetFactory({
|
|
110
|
+
assetsList,
|
|
111
|
+
feeData,
|
|
112
|
+
...config.plugin,
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
return { name, chainId, createAsset }
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// https://docs.metamask.io/wallet/reference/wallet_addethereumchain/
|
|
119
|
+
export const fromAddEthereumChainParameterToFactoryParams = (params) => {
|
|
120
|
+
assert(params.chainId, 'chainId is required')
|
|
121
|
+
assert(params.chainName, 'chainName is required')
|
|
122
|
+
assert(params.rpcUrls?.[0], 'rpcUrls is required')
|
|
123
|
+
assert(params.blockExplorerUrls?.[0], 'blockExplorerUrls is required')
|
|
124
|
+
assert(params.nativeCurrency, 'nativeCurrency is required')
|
|
125
|
+
assert(params.nativeCurrency.symbol, 'nativeCurrency.symbol is required')
|
|
126
|
+
assert(params.nativeCurrency.decimals, 'nativeCurrency.decimals is required')
|
|
127
|
+
|
|
128
|
+
const chainId = parseInt(params.chainId.replace('0x', ''), 16)
|
|
129
|
+
|
|
130
|
+
// obvious coloring:)
|
|
131
|
+
const color =
|
|
132
|
+
params.color ||
|
|
133
|
+
'#' +
|
|
134
|
+
(chainId % 256).toString(16) +
|
|
135
|
+
((chainId + 50) % 256).toString(16) +
|
|
136
|
+
((chainId + 100) % 256).toString(16)
|
|
137
|
+
return {
|
|
138
|
+
chainId,
|
|
139
|
+
meta: {
|
|
140
|
+
explorerUrl: params.blockExplorerUrls[0],
|
|
141
|
+
displayName: params.chainName,
|
|
142
|
+
displayTicker: params.nativeCurrency.name || params.nativeCurrency.symbol,
|
|
143
|
+
primaryColor: color,
|
|
144
|
+
gradientColors: [color, color],
|
|
145
|
+
gradientCoords: {
|
|
146
|
+
x1: '0%',
|
|
147
|
+
y1: '0%',
|
|
148
|
+
x2: '100%',
|
|
149
|
+
y2: '100%',
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
plugin: {
|
|
153
|
+
serverUrl: params.rpcUrls?.[0],
|
|
154
|
+
confirmationsNumber: 5,
|
|
155
|
+
monitorType: 'no-history',
|
|
156
|
+
isTestnet: params.isTestnet,
|
|
157
|
+
},
|
|
158
|
+
baseFeePerGas: params.baseFeePerGas,
|
|
159
|
+
gasPrice: params.gasPrice,
|
|
160
|
+
eip1559Enabled: params.eip1559Enabled,
|
|
161
|
+
}
|
|
162
|
+
}
|
package/src/create-asset.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { connectAssetsList } from '@exodus/assets'
|
|
2
2
|
import { pick } from '@exodus/basic-utils'
|
|
3
3
|
import bip44Constants from '@exodus/bip44-constants/by-ticker'
|
|
4
|
-
import {
|
|
4
|
+
import { getBalancesFactory } from './get-balances'
|
|
5
5
|
import { ClarityMonitor } from './tx-log/clarity-monitor'
|
|
6
6
|
import { EthereumNoHistoryMonitor } from './tx-log/ethereum-no-history-monitor'
|
|
7
7
|
import { EthereumMonitor } from './tx-log/ethereum-monitor'
|
|
@@ -108,7 +108,12 @@ export const createAssetFactory = ({
|
|
|
108
108
|
encodePublic: encodePublicFactory({ chainId, useEip1191ChainIdChecksum }),
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
const
|
|
111
|
+
const getBalances = getBalancesFactory({ monitorType })
|
|
112
|
+
|
|
113
|
+
const { createToken, getTokens } = createTokenFactory(
|
|
114
|
+
{ address, bip44, keys, getBalances },
|
|
115
|
+
assets
|
|
116
|
+
)
|
|
112
117
|
|
|
113
118
|
const addressHasHistory = addressHasHistoryFactory({ server })
|
|
114
119
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { isPolygonClaimUndelegate, isPolygonDelegate, isPolygonUndelegate } from './staking/matic'
|
|
2
2
|
import { createContract } from '@exodus/ethereum-lib'
|
|
3
3
|
|
|
4
|
-
import { getBalances } from './get-balances'
|
|
5
|
-
|
|
6
4
|
import { createStakingApi } from './staking-api'
|
|
5
|
+
import assert from 'minimalistic-assert'
|
|
7
6
|
|
|
8
7
|
const defaultTokenFeatures = {
|
|
9
8
|
isMaxFeeAsset: true,
|
|
@@ -30,8 +29,9 @@ const getPolygonActivityTxs = ({ txs }) =>
|
|
|
30
29
|
const getActivityTxs = ({ txs }) => txs.filter((tx) => !smallbalanceTx(tx))
|
|
31
30
|
|
|
32
31
|
const getCreateBaseToken =
|
|
33
|
-
(props) =>
|
|
32
|
+
({ getBalances, ...props }) =>
|
|
34
33
|
({ name, contract, features, ...tokenDef }) => {
|
|
34
|
+
assert(getBalances, 'getBalances is required')
|
|
35
35
|
const tokenSpecificFeatures = name === 'polygon' ? { staking: {} } : {}
|
|
36
36
|
const tokenSpecificApiFunctions =
|
|
37
37
|
name === 'polygon' ? { staking: createStakingApi({ network: name }) } : {}
|
package/src/get-balances.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { isRpcBalanceAsset } from '@exodus/ethereum-lib'
|
|
2
2
|
|
|
3
3
|
import { get } from 'lodash'
|
|
4
|
+
import assert from 'minimalistic-assert'
|
|
5
|
+
|
|
4
6
|
import {
|
|
5
7
|
getBalanceFromAccountState,
|
|
6
8
|
getBalanceFromTxLog,
|
|
@@ -30,36 +32,40 @@ const getUnstaked = ({ accountState, asset }) => {
|
|
|
30
32
|
* @param accountState the account state when the balance is loaded from RPC
|
|
31
33
|
* @returns {{balance}|null} an object with the balance or null if the balance is unknown
|
|
32
34
|
*/
|
|
33
|
-
export const
|
|
34
|
-
|
|
35
|
-
|
|
35
|
+
export const getBalancesFactory = ({ monitorType }) => {
|
|
36
|
+
assert(monitorType, 'monitorType is required')
|
|
37
|
+
return ({ asset, txLog, accountState }) => {
|
|
38
|
+
const unconfirmedReceived = getUnconfirmedReceivedBalance({ asset, txLog })
|
|
39
|
+
const unconfirmedSent = getUnconfirmedSentBalance({ asset, txLog })
|
|
36
40
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
// balance from txLog / rpc is considered to be total
|
|
42
|
+
const balanceWithoutUnconfirmedSent =
|
|
43
|
+
monitorType === 'no-history' || isRpcBalanceAsset(asset)
|
|
44
|
+
? getBalanceFromAccountState({ asset, accountState }).sub(unconfirmedSent)
|
|
45
|
+
: getBalanceFromTxLog({ txLog, asset })
|
|
41
46
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
const staked = getStaked({ asset, accountState })
|
|
48
|
+
const unstaking = getUnstaking({ asset, accountState })
|
|
49
|
+
const unstaked = getUnstaked({ asset, accountState })
|
|
50
|
+
const total = balanceWithoutUnconfirmedSent
|
|
51
|
+
const spendable = balanceWithoutUnconfirmedSent
|
|
52
|
+
.sub(unconfirmedReceived)
|
|
53
|
+
.sub(staked)
|
|
54
|
+
.sub(unstaking)
|
|
55
|
+
.sub(unstaked)
|
|
51
56
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
return {
|
|
58
|
+
// new
|
|
59
|
+
spendable,
|
|
60
|
+
total,
|
|
61
|
+
staked,
|
|
62
|
+
unstaking,
|
|
63
|
+
unstaked,
|
|
64
|
+
unconfirmedReceived,
|
|
65
|
+
unconfirmedSent,
|
|
66
|
+
// legacy
|
|
67
|
+
balance: total,
|
|
68
|
+
spendableBalance: spendable,
|
|
69
|
+
}
|
|
64
70
|
}
|
|
65
71
|
}
|
package/src/index.js
CHANGED
|
@@ -14,3 +14,7 @@ export * from './number-utils'
|
|
|
14
14
|
export { reasons as errorReasons, withErrorReason, EthLikeError } from './error-wrapper'
|
|
15
15
|
export { txSendFactory, getFeeInfo } from './tx-send'
|
|
16
16
|
export { createAssetFactory } from './create-asset'
|
|
17
|
+
export {
|
|
18
|
+
createAssetPluginFactory,
|
|
19
|
+
fromAddEthereumChainParameterToFactoryParams,
|
|
20
|
+
} from './create-asset-plugin-factory'
|
|
@@ -123,8 +123,8 @@ export class EthereumMonitor extends BaseMonitor {
|
|
|
123
123
|
// FIXME: Fetching unconfirmed txs from node helps to avoid
|
|
124
124
|
// flagging on-chain confirmed txs as dropped in the wallet.
|
|
125
125
|
// Once the real bug is found, remove this logic
|
|
126
|
-
const
|
|
127
|
-
const isTxConfirmed =
|
|
126
|
+
const rpcTx = await this.server.getTransactionByHash(tx.txId)
|
|
127
|
+
const isTxConfirmed = !!rpcTx?.blockNumber
|
|
128
128
|
this.logger.warn(`tx ${tx.txId} confirmed: ${isTxConfirmed}`)
|
|
129
129
|
await updateTx(tx, assetName, { isTxConfirmed })
|
|
130
130
|
}
|