@exodus/ethereum-api 8.34.3 → 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 +10 -0
- package/package.json +3 -3
- package/src/fee-utils.js +11 -0
- package/src/gas-estimation.js +11 -6
- package/src/get-fee-async.js +0 -14
- package/src/get-fee.js +26 -21
- package/src/index.js +2 -0
- package/src/staking/ethereum/api.js +1 -0
- package/src/staking/ethereum/service.js +114 -80
- package/src/staking/matic/matic-staking.md +53 -0
- package/src/staking/matic/service.js +189 -100
- package/src/staking/utils/index.js +30 -34
- package/src/tx-send/get-fee-info.js +31 -18
- package/src/tx-send/tx-send.js +25 -27
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
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
|
+
|
|
6
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)
|
|
7
17
|
|
|
8
18
|
|
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/fee-utils.js
CHANGED
|
@@ -54,3 +54,14 @@ export const ensureSaneEip1559GasPriceForTipGasPrice = ({ gasPrice, tipGasPrice
|
|
|
54
54
|
if (!tipGasPrice || tipGasPrice.lt(gasPrice)) return gasPrice
|
|
55
55
|
return tipGasPrice
|
|
56
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,26 +50,13 @@ 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 } = feeData
|
|
48
|
-
|
|
49
|
-
let gasPrice = customFee || resolveGasPrice({ feeData })
|
|
50
|
-
|
|
51
|
-
// The `gasPrice` must be at least the `tipGasPrice`.
|
|
52
|
-
if (eip1559Enabled) {
|
|
53
|
-
gasPrice = ensureSaneEip1559GasPriceForTipGasPrice({ 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
|
|
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'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { estimateGasLimit } from '../../gas-estimation.js'
|
|
2
2
|
import { createWatchTx as defaultCreateWatch } from '../../watch-tx.js'
|
|
3
3
|
import { stakingProviderClientFactory } from '../staking-provider-client.js'
|
|
4
|
-
import { amountToCurrency,
|
|
4
|
+
import { amountToCurrency, DISABLE_BALANCE_CHECKS, resolveFeeData } from '../utils/index.js'
|
|
5
5
|
import { EthereumStaking } from './api.js'
|
|
6
6
|
|
|
7
7
|
const WETH9_ADDRESS = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2'
|
|
@@ -20,8 +20,6 @@ const MINIMUM_DELEGATION_GAS_LIMIT = 180_000
|
|
|
20
20
|
|
|
21
21
|
const EXTRA_GAS_LIMIT = 20_000 // extra gas Limit to prevent tx failing if something change on pool state (till tx is in mempool)
|
|
22
22
|
|
|
23
|
-
const DISABLE_BALANCE_CHECKS = '0x0'
|
|
24
|
-
|
|
25
23
|
export function createEthereumStakingService({
|
|
26
24
|
asset,
|
|
27
25
|
assetClientInterface,
|
|
@@ -31,17 +29,27 @@ export function createEthereumStakingService({
|
|
|
31
29
|
const staking = new EthereumStaking(asset, undefined, asset.server)
|
|
32
30
|
const minAmount = staking.minAmount
|
|
33
31
|
|
|
34
|
-
async
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
const getTransactionProps = async ({ feeData, walletAccount }) => {
|
|
33
|
+
let address
|
|
34
|
+
;[address, feeData] = await Promise.all([
|
|
35
|
+
assetClientInterface.getReceiveAddress({
|
|
36
|
+
assetName: asset.name,
|
|
37
|
+
walletAccount,
|
|
38
|
+
}),
|
|
39
|
+
resolveFeeData({ asset, assetClientInterface, feeData }),
|
|
40
|
+
])
|
|
41
|
+
|
|
39
42
|
const delegatorAddress = address.toLowerCase()
|
|
43
|
+
return { delegatorAddress, feeData }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function delegate({ walletAccount, amount, feeData } = Object.create(null)) {
|
|
40
47
|
amount = amountToCurrency({ asset, amount })
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
let delegatorAddress
|
|
50
|
+
;({ feeData, delegatorAddress } = await getTransactionProps({ feeData, walletAccount }))
|
|
51
|
+
|
|
52
|
+
const { to, data } = await staking.stake({ amount })
|
|
45
53
|
|
|
46
54
|
console.log(
|
|
47
55
|
`delegator address ${delegatorAddress} staking ${amount.toDefaultString({
|
|
@@ -49,12 +57,14 @@ export function createEthereumStakingService({
|
|
|
49
57
|
})}`
|
|
50
58
|
)
|
|
51
59
|
|
|
52
|
-
const { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
53
|
-
delegatorAddress,
|
|
60
|
+
const { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee({
|
|
61
|
+
from: delegatorAddress,
|
|
54
62
|
to,
|
|
55
63
|
amount,
|
|
56
|
-
data
|
|
57
|
-
|
|
64
|
+
txInput: data,
|
|
65
|
+
feeData,
|
|
66
|
+
})
|
|
67
|
+
|
|
58
68
|
const txId = await prepareAndSendTx({
|
|
59
69
|
asset,
|
|
60
70
|
walletAccount,
|
|
@@ -64,6 +74,7 @@ export function createEthereumStakingService({
|
|
|
64
74
|
gasPrice,
|
|
65
75
|
gasLimit,
|
|
66
76
|
tipGasPrice,
|
|
77
|
+
feeData,
|
|
67
78
|
})
|
|
68
79
|
|
|
69
80
|
// Goerli is not supported
|
|
@@ -83,32 +94,36 @@ export function createEthereumStakingService({
|
|
|
83
94
|
resquestedAmount,
|
|
84
95
|
pendingAmount,
|
|
85
96
|
minAmount,
|
|
97
|
+
feeData,
|
|
86
98
|
}) {
|
|
87
99
|
const leftOver = pendingAmount.sub(resquestedAmount)
|
|
88
100
|
|
|
89
|
-
if (leftOver.isPositive && leftOver.lt(minAmount))
|
|
101
|
+
if (leftOver.isPositive && leftOver.lt(minAmount))
|
|
90
102
|
throw new Error(`Pending balance less than min stake amount ${minAmount}`)
|
|
91
|
-
}
|
|
92
103
|
|
|
93
104
|
const inactiveAmountToUnstake = pendingAmount.lte(resquestedAmount)
|
|
94
105
|
? pendingAmount
|
|
95
106
|
: resquestedAmount
|
|
107
|
+
|
|
108
|
+
feeData = await resolveFeeData({ asset, assetClientInterface, feeData })
|
|
109
|
+
|
|
96
110
|
const { to, data } = await staking.unstakePending({
|
|
97
111
|
address: delegatorAddress,
|
|
98
112
|
amount: inactiveAmountToUnstake,
|
|
99
113
|
})
|
|
100
114
|
|
|
101
|
-
const { fee, gasLimit, gasPrice, tipGasPrice } = await estimateTxFee(
|
|
102
|
-
delegatorAddress,
|
|
115
|
+
const { fee, gasLimit, gasPrice, tipGasPrice } = await estimateTxFee({
|
|
116
|
+
from: delegatorAddress,
|
|
103
117
|
to,
|
|
104
|
-
null,
|
|
105
|
-
data
|
|
106
|
-
|
|
118
|
+
amount: null,
|
|
119
|
+
txInput: data,
|
|
120
|
+
feeData,
|
|
121
|
+
})
|
|
107
122
|
|
|
108
123
|
return { to, txData: data, gasLimit, gasPrice, tipGasPrice, fee }
|
|
109
124
|
}
|
|
110
125
|
|
|
111
|
-
async function getUndelegateData({ delegatorAddress, resquestedAmount, pendingAmount }) {
|
|
126
|
+
async function getUndelegateData({ delegatorAddress, resquestedAmount, pendingAmount, feeData }) {
|
|
112
127
|
const canUnstake = resquestedAmount.gt(pendingAmount)
|
|
113
128
|
|
|
114
129
|
if (!canUnstake) {
|
|
@@ -117,17 +132,21 @@ export function createEthereumStakingService({
|
|
|
117
132
|
}
|
|
118
133
|
|
|
119
134
|
const activeAmountToUnstake = resquestedAmount.sub(pendingAmount)
|
|
135
|
+
|
|
136
|
+
feeData = await resolveFeeData({ asset, assetClientInterface, feeData })
|
|
137
|
+
|
|
120
138
|
const { to, data } = await staking.unstake({
|
|
121
139
|
address: delegatorAddress,
|
|
122
140
|
amount: activeAmountToUnstake,
|
|
123
141
|
})
|
|
124
142
|
|
|
125
|
-
const { fee, gasLimit, gasPrice, tipGasPrice } = await estimateTxFee(
|
|
126
|
-
delegatorAddress,
|
|
143
|
+
const { fee, gasLimit, gasPrice, tipGasPrice } = await estimateTxFee({
|
|
144
|
+
from: delegatorAddress,
|
|
127
145
|
to,
|
|
128
|
-
null,
|
|
129
|
-
data
|
|
130
|
-
|
|
146
|
+
amount: null,
|
|
147
|
+
txInput: data,
|
|
148
|
+
feeData,
|
|
149
|
+
})
|
|
131
150
|
|
|
132
151
|
return { to, txData: data, gasLimit, gasPrice, tipGasPrice, fee }
|
|
133
152
|
}
|
|
@@ -137,12 +156,10 @@ export function createEthereumStakingService({
|
|
|
137
156
|
* Fee estimation depends on the executed txs. Can be both.
|
|
138
157
|
* @returns total undelegete fee
|
|
139
158
|
*/
|
|
140
|
-
async function estimateUndelegate({ walletAccount, amount: resquestedAmount }) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
})
|
|
145
|
-
const delegatorAddress = address.toLowerCase()
|
|
159
|
+
async function estimateUndelegate({ walletAccount, amount: resquestedAmount, feeData }) {
|
|
160
|
+
let delegatorAddress
|
|
161
|
+
;({ feeData, delegatorAddress } = await getTransactionProps({ feeData, walletAccount }))
|
|
162
|
+
|
|
146
163
|
const pendingAmount = await staking.pendingBalanceOf(delegatorAddress)
|
|
147
164
|
|
|
148
165
|
let undelegatePendingFee = asset.currency.ZERO
|
|
@@ -155,6 +172,7 @@ export function createEthereumStakingService({
|
|
|
155
172
|
resquestedAmount,
|
|
156
173
|
pendingAmount,
|
|
157
174
|
minAmount,
|
|
175
|
+
feeData,
|
|
158
176
|
})
|
|
159
177
|
undelegatePendingFee = fee
|
|
160
178
|
} catch (err) {
|
|
@@ -168,12 +186,15 @@ export function createEthereumStakingService({
|
|
|
168
186
|
resquestedAmount,
|
|
169
187
|
pendingAmount,
|
|
170
188
|
minAmount,
|
|
189
|
+
feeData,
|
|
171
190
|
})
|
|
172
191
|
|
|
173
192
|
return undelegatePendingFee.add(undelegateFee)
|
|
174
193
|
}
|
|
175
194
|
|
|
176
|
-
async function undelegate(
|
|
195
|
+
async function undelegate(
|
|
196
|
+
{ walletAccount, amount, feeData, waitForConfirmation = true } = Object.create(null)
|
|
197
|
+
) {
|
|
177
198
|
/*
|
|
178
199
|
unstakePending balance (not yet in validator) + unstake balance (in validator)
|
|
179
200
|
1. give priority to unstakePending (based on the amount)
|
|
@@ -181,11 +202,9 @@ export function createEthereumStakingService({
|
|
|
181
202
|
*/
|
|
182
203
|
const resquestedAmount = amountToCurrency({ asset, amount })
|
|
183
204
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
})
|
|
188
|
-
const delegatorAddress = address.toLowerCase()
|
|
205
|
+
let delegatorAddress
|
|
206
|
+
;({ feeData, delegatorAddress } = await getTransactionProps({ feeData, walletAccount }))
|
|
207
|
+
|
|
189
208
|
const pendingAmount = await staking.pendingBalanceOf(delegatorAddress)
|
|
190
209
|
|
|
191
210
|
console.log(
|
|
@@ -201,14 +220,24 @@ export function createEthereumStakingService({
|
|
|
201
220
|
resquestedAmount,
|
|
202
221
|
pendingAmount,
|
|
203
222
|
minAmount,
|
|
223
|
+
feeData,
|
|
204
224
|
})
|
|
205
225
|
|
|
206
226
|
txId = await prepareAndSendTx({
|
|
207
227
|
asset,
|
|
208
228
|
walletAccount,
|
|
209
|
-
waitForConfirmation
|
|
229
|
+
waitForConfirmation,
|
|
210
230
|
...undelegatePendingData,
|
|
231
|
+
feeData,
|
|
211
232
|
})
|
|
233
|
+
|
|
234
|
+
// If we are waiting for confirmation, then sufficient time
|
|
235
|
+
// will have elapsed for us to re-estimate `feeData`.
|
|
236
|
+
//
|
|
237
|
+
// NOTE: This will invalidate previous transaction fee estimates!
|
|
238
|
+
if (waitForConfirmation) {
|
|
239
|
+
feeData = await assetClientInterface.getFeeData({ assetName: asset.name })
|
|
240
|
+
}
|
|
212
241
|
}
|
|
213
242
|
|
|
214
243
|
// may need also to unstake
|
|
@@ -216,6 +245,7 @@ export function createEthereumStakingService({
|
|
|
216
245
|
delegatorAddress,
|
|
217
246
|
resquestedAmount,
|
|
218
247
|
pendingAmount,
|
|
248
|
+
feeData,
|
|
219
249
|
})
|
|
220
250
|
|
|
221
251
|
if (undelegateData.fee) {
|
|
@@ -223,6 +253,7 @@ export function createEthereumStakingService({
|
|
|
223
253
|
asset,
|
|
224
254
|
walletAccount,
|
|
225
255
|
...undelegateData,
|
|
256
|
+
feeData,
|
|
226
257
|
})
|
|
227
258
|
}
|
|
228
259
|
|
|
@@ -238,26 +269,24 @@ export function createEthereumStakingService({
|
|
|
238
269
|
return txId
|
|
239
270
|
}
|
|
240
271
|
|
|
241
|
-
async function claimUndelegatedBalance({ walletAccount } = Object.create(null)) {
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
assetName: asset.name,
|
|
245
|
-
walletAccount,
|
|
246
|
-
})
|
|
247
|
-
const delegatorAddress = address.toLowerCase()
|
|
272
|
+
async function claimUndelegatedBalance({ walletAccount, feeData } = Object.create(null)) {
|
|
273
|
+
let delegatorAddress
|
|
274
|
+
;({ feeData, delegatorAddress } = await getTransactionProps({ feeData, walletAccount }))
|
|
248
275
|
|
|
249
276
|
const withdrawRequest = await staking.claimWithdrawRequest({
|
|
250
277
|
address: delegatorAddress,
|
|
251
278
|
})
|
|
279
|
+
|
|
252
280
|
if (withdrawRequest) {
|
|
253
281
|
const { to, data } = withdrawRequest
|
|
254
282
|
|
|
255
|
-
const { gasLimit, gasPrice, tipGasPrice } = await estimateTxFee(
|
|
256
|
-
delegatorAddress,
|
|
283
|
+
const { gasLimit, gasPrice, tipGasPrice } = await estimateTxFee({
|
|
284
|
+
from: delegatorAddress,
|
|
257
285
|
to,
|
|
258
|
-
null,
|
|
259
|
-
data
|
|
260
|
-
|
|
286
|
+
amount: null,
|
|
287
|
+
txInput: data,
|
|
288
|
+
feeData,
|
|
289
|
+
})
|
|
261
290
|
return prepareAndSendTx({
|
|
262
291
|
asset,
|
|
263
292
|
walletAccount,
|
|
@@ -266,24 +295,21 @@ export function createEthereumStakingService({
|
|
|
266
295
|
gasLimit,
|
|
267
296
|
gasPrice,
|
|
268
297
|
tipGasPrice,
|
|
298
|
+
feeData,
|
|
269
299
|
})
|
|
270
300
|
}
|
|
271
301
|
}
|
|
272
302
|
|
|
273
|
-
async function estimateDelegateOperation({ walletAccount, operation, args }) {
|
|
303
|
+
async function estimateDelegateOperation({ walletAccount, operation, args, feeData }) {
|
|
274
304
|
const requestedAmount = args.amount
|
|
275
305
|
? amountToCurrency({ asset, amount: args.amount })
|
|
276
306
|
: asset.currency.ZERO
|
|
277
307
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
}
|
|
308
|
+
let delegatorAddress
|
|
309
|
+
;({ delegatorAddress, feeData } = await getTransactionProps({ feeData, walletAccount }))
|
|
281
310
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
walletAccount,
|
|
285
|
-
})
|
|
286
|
-
const delegatorAddress = address.toLowerCase()
|
|
311
|
+
if (operation === 'undelegate')
|
|
312
|
+
return estimateUndelegate({ walletAccount, amount: requestedAmount, feeData })
|
|
287
313
|
|
|
288
314
|
const NAMING_MAP = {
|
|
289
315
|
delegate: 'stake',
|
|
@@ -293,14 +319,18 @@ export function createEthereumStakingService({
|
|
|
293
319
|
|
|
294
320
|
const delegateOperation = staking[NAMING_MAP[operation]].bind(staking)
|
|
295
321
|
|
|
296
|
-
if (!delegateOperation)
|
|
297
|
-
throw new Error('Invalid staking operation')
|
|
298
|
-
}
|
|
322
|
+
if (!delegateOperation) throw new Error('Invalid staking operation')
|
|
299
323
|
|
|
300
324
|
const { amount, data } = await delegateOperation({ ...args, amount: requestedAmount })
|
|
301
325
|
|
|
302
326
|
const { fee } = await (operation === 'claimUndelegatedBalance'
|
|
303
|
-
? estimateTxFee(
|
|
327
|
+
? estimateTxFee({
|
|
328
|
+
from: delegatorAddress,
|
|
329
|
+
to: staking.accountingAddress,
|
|
330
|
+
amount,
|
|
331
|
+
txInput: data,
|
|
332
|
+
feeData,
|
|
333
|
+
})
|
|
304
334
|
: // The `gasUsed` of a delegation transaction can vary
|
|
305
335
|
// significantly depending upon whether it will result
|
|
306
336
|
// in the activation of new slots (i.e. validator creation).
|
|
@@ -314,17 +344,18 @@ export function createEthereumStakingService({
|
|
|
314
344
|
// 2. Originate the transaction from the WETH contract,
|
|
315
345
|
// which guarantees deep native ether liquidity which
|
|
316
346
|
// exceeds any rational user deposit.
|
|
317
|
-
estimateTxFee(
|
|
318
|
-
WETH9_ADDRESS,
|
|
319
|
-
staking.poolAddress,
|
|
320
|
-
amount.add(asset.currency.defaultUnit(SLOT_ACTIVATION_PROXIMITY_ETH)),
|
|
321
|
-
data
|
|
322
|
-
|
|
347
|
+
estimateTxFee({
|
|
348
|
+
from: WETH9_ADDRESS,
|
|
349
|
+
to: staking.poolAddress,
|
|
350
|
+
amount: amount.add(asset.currency.defaultUnit(SLOT_ACTIVATION_PROXIMITY_ETH)),
|
|
351
|
+
txInput: data,
|
|
352
|
+
feeData,
|
|
353
|
+
}))
|
|
323
354
|
|
|
324
355
|
return fee
|
|
325
356
|
}
|
|
326
357
|
|
|
327
|
-
async function estimateTxFee(from, to, amount, txInput) {
|
|
358
|
+
async function estimateTxFee({ from, to, amount, txInput, feeData }) {
|
|
328
359
|
amount = amount || asset.currency.ZERO
|
|
329
360
|
from = from.toLowerCase()
|
|
330
361
|
|
|
@@ -347,17 +378,14 @@ export function createEthereumStakingService({
|
|
|
347
378
|
EthereumStaking.isDelegationTransactonCalldata(txInput) ? MINIMUM_DELEGATION_GAS_LIMIT : 0
|
|
348
379
|
)
|
|
349
380
|
|
|
350
|
-
const
|
|
351
|
-
amount,
|
|
381
|
+
const getFeeResult = await asset.api.getFee({
|
|
352
382
|
asset,
|
|
353
|
-
|
|
383
|
+
feeData,
|
|
354
384
|
gasLimit,
|
|
385
|
+
amount,
|
|
355
386
|
})
|
|
356
387
|
|
|
357
|
-
return {
|
|
358
|
-
...feeData,
|
|
359
|
-
gasLimit,
|
|
360
|
-
}
|
|
388
|
+
return { ...getFeeResult, gasLimit }
|
|
361
389
|
}
|
|
362
390
|
|
|
363
391
|
/** Returns the minimum possible amount that can be staked. */
|
|
@@ -369,9 +397,12 @@ export function createEthereumStakingService({
|
|
|
369
397
|
async function getDelegateSelectAllAmount({
|
|
370
398
|
walletAccount,
|
|
371
399
|
spendableForStaking = asset.currency.ZERO,
|
|
400
|
+
feeData,
|
|
372
401
|
}) {
|
|
373
402
|
const minAmount = getMinAmount()
|
|
374
403
|
|
|
404
|
+
;({ feeData } = await getTransactionProps({ feeData, walletAccount }))
|
|
405
|
+
|
|
375
406
|
// If the caller hasn't specified a value of `spendableForStaking`
|
|
376
407
|
// which satisfies the minimum amount, then we'll coerce this value
|
|
377
408
|
// up to the minimum amount so we can at least provide a rational
|
|
@@ -389,6 +420,7 @@ export function createEthereumStakingService({
|
|
|
389
420
|
args: {
|
|
390
421
|
amount: spendableForStaking,
|
|
391
422
|
},
|
|
423
|
+
feeData,
|
|
392
424
|
})
|
|
393
425
|
|
|
394
426
|
// If the `spendableForStaking` is insufficient to cover both the
|
|
@@ -420,6 +452,7 @@ export function createEthereumStakingService({
|
|
|
420
452
|
gasLimit,
|
|
421
453
|
tipGasPrice,
|
|
422
454
|
waitForConfirmation = false,
|
|
455
|
+
feeData,
|
|
423
456
|
} = Object.create(null)
|
|
424
457
|
) {
|
|
425
458
|
const sendTxArgs = {
|
|
@@ -436,6 +469,7 @@ export function createEthereumStakingService({
|
|
|
436
469
|
// HACK: Override the `tipGasPrice` to use a custom `maxPriorityFeePerGas`.
|
|
437
470
|
tipGasPrice,
|
|
438
471
|
},
|
|
472
|
+
feeData,
|
|
439
473
|
}
|
|
440
474
|
|
|
441
475
|
const { txId } = await asset.baseAsset.api.sendTx(sendTxArgs)
|