@exodus/ethereum-api 8.34.2 → 8.34.4
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 +20 -0
- package/package.json +3 -3
- package/src/create-asset.js +1 -2
- package/src/custom-fees.js +40 -10
- package/src/fee-utils.js +17 -3
- package/src/gas-estimation.js +11 -6
- package/src/get-fee-async.js +0 -14
- package/src/get-fee.js +29 -35
- package/src/index.js +2 -0
- package/src/staking/ethereum/api.js +1 -0
- package/src/staking/ethereum/service.js +116 -92
- package/src/staking/matic/matic-staking.md +53 -0
- package/src/staking/matic/service.js +189 -102
- package/src/staking/utils/index.js +26 -60
- package/src/tx-send/get-fee-info.js +34 -9
- package/src/tx-send/tx-send.js +41 -30
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
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.34.4](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.34.3...@exodus/ethereum-api@8.34.4) (2025-05-07)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
* fix: remove fixed gas limit tokens on evm (#5543)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
## [8.34.3](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.34.2...@exodus/ethereum-api@8.34.3) (2025-04-29)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
* fix: prevent insufficient evm custom fees on eip1559Enabled networks (#5519)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
## [8.34.2](https://github.com/ExodusMovement/assets/compare/@exodus/ethereum-api@8.34.1...@exodus/ethereum-api@8.34.2) (2025-04-18)
|
|
7
27
|
|
|
8
28
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "8.34.
|
|
3
|
+
"version": "8.34.4",
|
|
4
4
|
"description": "Transaction monitors, fee monitors, RPC with the blockchain node, and other networking code for Ethereum and EVM-based blockchains",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.js",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"@exodus/bip44-constants": "^195.0.0",
|
|
29
29
|
"@exodus/crypto": "^1.0.0-rc.13",
|
|
30
30
|
"@exodus/currency": "^6.0.1",
|
|
31
|
-
"@exodus/ethereum-lib": "^5.10.
|
|
31
|
+
"@exodus/ethereum-lib": "^5.10.2",
|
|
32
32
|
"@exodus/ethereum-meta": "^2.5.0",
|
|
33
33
|
"@exodus/ethereumholesky-meta": "^2.0.2",
|
|
34
34
|
"@exodus/ethereumjs": "^1.0.0",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
"type": "git",
|
|
65
65
|
"url": "git+https://github.com/ExodusMovement/assets.git"
|
|
66
66
|
},
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "ef3710f80aaff8c5d84bc53026800f9a1c0e029f"
|
|
68
68
|
}
|
package/src/create-asset.js
CHANGED
|
@@ -28,7 +28,7 @@ import { createEvmServer, ValidMonitorTypes } from './exodus-eth-server/index.js
|
|
|
28
28
|
import { createFeeData } from './fee-data-factory.js'
|
|
29
29
|
import { createGetBalanceForAddress } from './get-balance-for-address.js'
|
|
30
30
|
import { getBalancesFactory } from './get-balances.js'
|
|
31
|
-
import {
|
|
31
|
+
import { getFeeFactory } from './get-fee.js'
|
|
32
32
|
import getFeeAsyncFactory from './get-fee-async.js'
|
|
33
33
|
import { createEthereumHooks } from './hooks/index.js'
|
|
34
34
|
import { estimateL1DataFeeFactory, getL1GetFeeFactory } from './optimism-gas/index.js'
|
|
@@ -323,7 +323,6 @@ export const createAssetFactory = ({
|
|
|
323
323
|
server,
|
|
324
324
|
...(erc20FuelBuffer && { erc20FuelBuffer }),
|
|
325
325
|
...(fuelThreshold && { fuelThreshold: asset.currency.defaultUnit(fuelThreshold) }),
|
|
326
|
-
getEffectiveGasPrice,
|
|
327
326
|
}
|
|
328
327
|
return overrideCallback({
|
|
329
328
|
asset: fullAsset,
|
package/src/custom-fees.js
CHANGED
|
@@ -2,21 +2,51 @@ import NumberUnit from '@exodus/currency'
|
|
|
2
2
|
|
|
3
3
|
import { resolveGasPrice } from './fee-utils.js'
|
|
4
4
|
|
|
5
|
+
const calculateMinCustomFee = ({ feeData }) => {
|
|
6
|
+
const { eip1559Enabled, baseFeePerGas, gasPriceMinimumRate } = feeData
|
|
7
|
+
|
|
8
|
+
// On `eip1559Enabled` networks, the `baseFeePerGas` is
|
|
9
|
+
// the minimum a transaction should be sent using, else
|
|
10
|
+
// get rejected by the RPC.
|
|
11
|
+
if (eip1559Enabled && baseFeePerGas) return baseFeePerGas.mul(Math.max(gasPriceMinimumRate, 1))
|
|
12
|
+
|
|
13
|
+
// On legacy networks, senders are permitted to send
|
|
14
|
+
// transactions below the `baseFeePerGas` (since these
|
|
15
|
+
// would not be rejected by the RPC - in fact, it was
|
|
16
|
+
// even possible to mine transactions with a `gasPrice`
|
|
17
|
+
// of `0`!).
|
|
18
|
+
return resolveGasPrice({ feeData }).mul(gasPriceMinimumRate)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const calculateRecommendedCustomFee = ({ min, feeData }) => {
|
|
22
|
+
const recommended = resolveGasPrice({ feeData })
|
|
23
|
+
// The `recommended` fee must be at least the `min`.
|
|
24
|
+
return recommended.gt(min) ? recommended : min
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const calculateMaxCustomFee = ({ feeData, recommended }) => {
|
|
28
|
+
const { gasPriceMinimumRate, gasPriceMaximumRate } = feeData
|
|
29
|
+
|
|
30
|
+
const maxGasPrice = resolveGasPrice({ feeData }).mul(
|
|
31
|
+
Math.max(gasPriceMaximumRate, gasPriceMinimumRate)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
// When calculating the `max` custom fee, the returned fee
|
|
35
|
+
// must be at least the `recommended` price.
|
|
36
|
+
return recommended.gt(maxGasPrice) ? recommended : maxGasPrice
|
|
37
|
+
}
|
|
38
|
+
|
|
5
39
|
export const createCustomFeesApi = ({ baseAsset }) => {
|
|
6
40
|
const Gwei = baseAsset.currency.units.Gwei
|
|
7
41
|
return {
|
|
8
42
|
getRecommendedMinMaxFeeUnitPrices: ({ feeData }) => {
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
const calculateFeeUnitPrice = (feeUnitPrice, multiplier = 1) =>
|
|
14
|
-
feeUnitPrice.mul(multiplier).toNumber(Gwei)
|
|
15
|
-
|
|
43
|
+
const min = calculateMinCustomFee({ feeData })
|
|
44
|
+
const recommended = calculateRecommendedCustomFee({ feeData, min })
|
|
45
|
+
const max = calculateMaxCustomFee({ feeData, recommended })
|
|
16
46
|
return {
|
|
17
|
-
recommended:
|
|
18
|
-
min:
|
|
19
|
-
max:
|
|
47
|
+
recommended: recommended.toNumber(Gwei),
|
|
48
|
+
min: min.toNumber(Gwei),
|
|
49
|
+
max: max.toNumber(Gwei),
|
|
20
50
|
}
|
|
21
51
|
},
|
|
22
52
|
unit: 'gwei/gas',
|
package/src/fee-utils.js
CHANGED
|
@@ -41,9 +41,7 @@ export const rewriteFeeConfig = ({ feeAsset, feeConfig, feeData }) => {
|
|
|
41
41
|
export const resolveGasPrice = ({ feeData }) => {
|
|
42
42
|
assert(feeData, 'feeData is required')
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (eip1559Enabled && feeData.useBaseGasPrice) {
|
|
44
|
+
if (feeData.eip1559Enabled) {
|
|
47
45
|
return feeData.tipGasPrice
|
|
48
46
|
? feeData.baseFeePerGas.add(feeData.tipGasPrice)
|
|
49
47
|
: feeData.baseFeePerGas
|
|
@@ -51,3 +49,19 @@ export const resolveGasPrice = ({ feeData }) => {
|
|
|
51
49
|
|
|
52
50
|
return feeData.gasPrice
|
|
53
51
|
}
|
|
52
|
+
|
|
53
|
+
export const ensureSaneEip1559GasPriceForTipGasPrice = ({ gasPrice, tipGasPrice }) => {
|
|
54
|
+
if (!tipGasPrice || tipGasPrice.lt(gasPrice)) return gasPrice
|
|
55
|
+
return tipGasPrice
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export const getNormalizedFeeDataForCustomFee = ({ customFee, feeData }) => {
|
|
59
|
+
const { eip1559Enabled } = feeData
|
|
60
|
+
// We must ensure maxPriorityFeePerGas <= maxFeePerGas or our transaction library throws an error
|
|
61
|
+
// It's a bit counterintuitive since maxPriorityFeePerGas should only contain the tip,
|
|
62
|
+
// so we should be subtracting the base gas price from the custom gas price to keep just the tip
|
|
63
|
+
// but the fee is also limited by our maxFeePerGas above, so that implicitly captures the max tip.
|
|
64
|
+
// Setting this tipGasPrice to undefined will result in a legacy transaction (not an EIP1559 anymore)
|
|
65
|
+
if (eip1559Enabled && customFee) return { ...feeData, tipGasPrice: customFee }
|
|
66
|
+
return feeData
|
|
67
|
+
}
|
package/src/gas-estimation.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { currency2buffer, isEthereumLikeToken } from '@exodus/ethereum-lib'
|
|
2
2
|
import { bufferToHex, toBuffer } from '@exodus/ethereumjs/util'
|
|
3
|
-
import BN from 'bn.js'
|
|
4
3
|
|
|
5
4
|
import { estimateGas, isContractAddressCached } from './eth-like-util.js'
|
|
6
5
|
|
|
@@ -11,6 +10,11 @@ const GAS_PER_NON_ZERO_BYTE = 16
|
|
|
11
10
|
|
|
12
11
|
export const DEFAULT_CONTRACT_GAS_LIMIT = 1e6
|
|
13
12
|
|
|
13
|
+
// HACK: RPCs generally provide imprecise estimates
|
|
14
|
+
// for `gasUsed` (often these are insufficient).
|
|
15
|
+
export const scaleGasLimitEstimate = ({ estimatedGasLimit, extraPercentage = EXTRA_PERCENTAGE }) =>
|
|
16
|
+
Number((BigInt(estimatedGasLimit) * BigInt(100 + extraPercentage)) / BigInt(100))
|
|
17
|
+
|
|
14
18
|
// Starting with geth v1.9.14, if gasPrice is set for eth_estimateGas call, the call allowance will
|
|
15
19
|
// be calculated with account's balance divided by gasPrice. If user's balance is too low,
|
|
16
20
|
// the gasEstimation will fail. If gasPrice is set to '0x0', the account's balance is not
|
|
@@ -22,7 +26,7 @@ export async function estimateGasLimit(
|
|
|
22
26
|
amount,
|
|
23
27
|
data,
|
|
24
28
|
gasPrice = '0x',
|
|
25
|
-
extraPercentage
|
|
29
|
+
extraPercentage
|
|
26
30
|
) {
|
|
27
31
|
const opts = {
|
|
28
32
|
from: fromAddress,
|
|
@@ -33,10 +37,11 @@ export async function estimateGasLimit(
|
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
const estimatedGas = await estimateGas({ asset, ...opts })
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
|
|
41
|
+
return scaleGasLimitEstimate({
|
|
42
|
+
estimatedGasLimit: BigInt(estimatedGas),
|
|
43
|
+
extraPercentage,
|
|
44
|
+
})
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
export function resolveDefaultTxInput({ asset, toAddress, amount }) {
|
package/src/get-fee-async.js
CHANGED
|
@@ -5,14 +5,6 @@ import { EXTRA_PERCENTAGE, fetchGasLimit, resolveDefaultTxInput } from './gas-es
|
|
|
5
5
|
import { getFeeFactory } from './get-fee.js'
|
|
6
6
|
import { getNftArguments } from './nft-utils.js'
|
|
7
7
|
|
|
8
|
-
const FIXED_TRANSFER_GAS_LIMIT_ASSETS = new Set([
|
|
9
|
-
'amp',
|
|
10
|
-
'tetherusd',
|
|
11
|
-
'usdcoin',
|
|
12
|
-
'snx',
|
|
13
|
-
'geminidollar',
|
|
14
|
-
])
|
|
15
|
-
|
|
16
8
|
const UNKNOWN_ADDRESS_EXTRA_PERCENTAGE = {
|
|
17
9
|
usdt_bsc_ddedf0f8: 80,
|
|
18
10
|
}
|
|
@@ -24,12 +16,6 @@ export async function resolveExtraPercentage({ asset, fromAddress, toAddress })
|
|
|
24
16
|
|
|
25
17
|
const isToken = asset.name !== asset.baseAsset.name
|
|
26
18
|
|
|
27
|
-
const isFixedGasLimitToken = isToken && FIXED_TRANSFER_GAS_LIMIT_ASSETS.has(asset.name)
|
|
28
|
-
|
|
29
|
-
if (isFixedGasLimitToken) {
|
|
30
|
-
return 0
|
|
31
|
-
}
|
|
32
|
-
|
|
33
19
|
// calling forwarder contracts with a bumped gas limit causes 'Out Of Gas' error on chain
|
|
34
20
|
const hasForwarderContract =
|
|
35
21
|
!isToken && toAddress ? await isForwarderContractCached({ asset, address: toAddress }) : false
|
package/src/get-fee.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { calculateBumpedGasPrice, calculateExtraEth } from '@exodus/ethereum-lib'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
ensureSaneEip1559GasPriceForTipGasPrice,
|
|
5
|
+
getNormalizedFeeDataForCustomFee,
|
|
6
|
+
resolveGasPrice,
|
|
7
|
+
} from './fee-utils.js'
|
|
4
8
|
|
|
5
9
|
// Move to meta?
|
|
6
10
|
const taxes = {
|
|
@@ -21,6 +25,20 @@ const getExtraFeeData = ({ asset, amount }) => {
|
|
|
21
25
|
}
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
export const getFeeFactoryGasPrices = ({ customFee, feeData }) => {
|
|
29
|
+
feeData = getNormalizedFeeDataForCustomFee({ customFee, feeData })
|
|
30
|
+
const gasPrice = customFee || resolveGasPrice({ feeData })
|
|
31
|
+
const { tipGasPrice, eip1559Enabled } = feeData
|
|
32
|
+
|
|
33
|
+
// The `gasPrice` must be at least the `tipGasPrice`.
|
|
34
|
+
return {
|
|
35
|
+
gasPrice: eip1559Enabled
|
|
36
|
+
? ensureSaneEip1559GasPriceForTipGasPrice({ gasPrice, tipGasPrice })
|
|
37
|
+
: gasPrice,
|
|
38
|
+
feeData,
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
24
42
|
export const getFeeFactory =
|
|
25
43
|
({ gasLimit: defaultGasLimit }) =>
|
|
26
44
|
({
|
|
@@ -32,32 +50,19 @@ export const getFeeFactory =
|
|
|
32
50
|
amount,
|
|
33
51
|
calculateEffectiveFee,
|
|
34
52
|
}) => {
|
|
35
|
-
const {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// must also enforce the relationship here!
|
|
43
|
-
if (eip1559Enabled && customFee) {
|
|
44
|
-
feeData = { ...feeData, tipGasPrice: customFee }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const { baseFeePerGas, tipGasPrice, useBaseGasPrice } = feeData
|
|
48
|
-
|
|
49
|
-
let gasPrice = customFee || resolveGasPrice({ feeData })
|
|
50
|
-
|
|
51
|
-
// The `gasPrice` must be at least the `tipGasPrice`.
|
|
52
|
-
if (eip1559Enabled && tipGasPrice && gasPrice.lt(tipGasPrice)) {
|
|
53
|
-
gasPrice = tipGasPrice
|
|
54
|
-
}
|
|
53
|
+
const {
|
|
54
|
+
feeData: { baseFeePerGas, tipGasPrice, eip1559Enabled },
|
|
55
|
+
gasPrice,
|
|
56
|
+
} = getFeeFactoryGasPrices({
|
|
57
|
+
customFee,
|
|
58
|
+
feeData,
|
|
59
|
+
})
|
|
55
60
|
|
|
56
61
|
const gasLimit = providedGasLimit || asset.gasLimit || defaultGasLimit
|
|
57
62
|
|
|
58
63
|
// When explicitly opting into EIP-1559 transactions,
|
|
59
64
|
// lock in the `tipGasPrice` we used to compute the fees.
|
|
60
|
-
const maybeReturnTipGasPrice = eip1559Enabled
|
|
65
|
+
const maybeReturnTipGasPrice = eip1559Enabled ? { tipGasPrice } : null
|
|
61
66
|
|
|
62
67
|
const extraFeeData = getExtraFeeData({ asset, amount })
|
|
63
68
|
if (calculateEffectiveFee && eip1559Enabled) {
|
|
@@ -78,26 +83,15 @@ export const getFeeFactory =
|
|
|
78
83
|
return { ...maybeReturnTipGasPrice, fee, gasPrice, extraFeeData }
|
|
79
84
|
}
|
|
80
85
|
|
|
81
|
-
// Used in BE
|
|
82
|
-
export const getEffectiveGasPrice = ({ feeData }) => {
|
|
83
|
-
const { baseFeePerGas, tipGasPrice, gasPrice: maxFeePerGas, eip1559Enabled } = feeData
|
|
84
|
-
|
|
85
|
-
if (!eip1559Enabled) {
|
|
86
|
-
return maxFeePerGas
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const gasPrice = baseFeePerGas.add(tipGasPrice)
|
|
90
|
-
return gasPrice.lt(maxFeePerGas) ? gasPrice : maxFeePerGas
|
|
91
|
-
}
|
|
92
|
-
|
|
93
86
|
// Used in Mobile
|
|
94
87
|
export const getExtraFeeForBump = ({ tx, feeData, balance, unconfirmedBalance }) => {
|
|
95
88
|
if (!balance || !unconfirmedBalance) return null
|
|
96
|
-
const { gasPrice: currentGasPrice, eip1559Enabled } = feeData
|
|
89
|
+
const { gasPrice: currentGasPrice, eip1559Enabled, baseFeePerGas: currentBaseFee } = feeData
|
|
97
90
|
const { bumpedGasPrice } = calculateBumpedGasPrice({
|
|
98
91
|
baseAsset: 'ethereum',
|
|
99
92
|
tx,
|
|
100
93
|
currentGasPrice,
|
|
94
|
+
currentBaseFee,
|
|
101
95
|
eip1559Enabled,
|
|
102
96
|
})
|
|
103
97
|
return calculateExtraEth({
|
package/src/index.js
CHANGED
|
@@ -81,6 +81,8 @@ export {
|
|
|
81
81
|
|
|
82
82
|
export { reasons as errorReasons, withErrorReason, EthLikeError } from './error-wrapper.js'
|
|
83
83
|
|
|
84
|
+
// TODO: `getFeeInfo` is not consumed by third parties and should
|
|
85
|
+
// be considered an internal API.
|
|
84
86
|
export { txSendFactory, getFeeInfo } from './tx-send/index.js'
|
|
85
87
|
|
|
86
88
|
export { createAssetFactory } from './create-asset.js'
|