@exodus/ethereum-api 8.34.7 → 8.34.8
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 +0 -20
- package/package.json +2 -2
- package/src/create-asset.js +2 -1
- package/src/custom-fees.js +10 -40
- package/src/etherscan/ws.js +2 -1
- package/src/exodus-eth-server/ws.js +2 -1
- package/src/fee-utils.js +3 -17
- package/src/gas-estimation.js +6 -11
- package/src/get-fee.js +35 -29
- package/src/index.js +0 -2
- package/src/staking/ethereum/api.js +0 -1
- package/src/staking/ethereum/service.js +92 -116
- package/src/staking/matic/matic-staking.md +0 -53
- package/src/staking/matic/service.js +102 -189
- package/src/staking/utils/index.js +60 -26
- package/src/tx-send/get-fee-info.js +9 -34
- package/src/tx-send/nonce-utils.js +1 -2
- package/src/tx-send/tx-send.js +31 -73
- package/src/websocket/index.android.js +2 -0
- package/src/websocket/index.ios.js +2 -0
- package/src/websocket/index.js +2 -0
- package/src/websocket/websocket.cjs +21 -0
|
@@ -1,140 +1,71 @@
|
|
|
1
|
-
import
|
|
1
|
+
import BN from 'bn.js'
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { getServer } from '../../exodus-eth-server/index.js'
|
|
4
|
+
import { estimateGasLimit } from '../../gas-estimation.js'
|
|
5
5
|
import { createWatchTx as defaultCreateWatch } from '../../watch-tx.js'
|
|
6
6
|
import { stakingProviderClientFactory } from '../staking-provider-client.js'
|
|
7
|
-
import {
|
|
8
|
-
amountToCurrency,
|
|
9
|
-
DISABLE_BALANCE_CHECKS,
|
|
10
|
-
resolveFeeData as defaultResolveFeeData,
|
|
11
|
-
} from '../utils/index.js'
|
|
7
|
+
import { amountToCurrency, getEvmStakingServiceFee } from '../utils/index.js'
|
|
12
8
|
import { MaticStakingApi } from './api.js'
|
|
13
9
|
|
|
14
|
-
const
|
|
15
|
-
|
|
10
|
+
const DISABLE_BALANCE_CHECKS = '0x0'
|
|
11
|
+
const MAX_PRIORITY_FEE_PER_GAS = '0.06 Gwei' // semi-urgent
|
|
16
12
|
|
|
17
|
-
// TODO: This should be `createMaticStakingService` to avoid confusion with Polygon staking.
|
|
18
13
|
export function createPolygonStakingService({
|
|
19
14
|
assetClientInterface,
|
|
20
15
|
createWatchTx = defaultCreateWatch,
|
|
21
|
-
stakingProvider = stakingProviderClientFactory(),
|
|
22
16
|
}) {
|
|
17
|
+
const stakingApi = new MaticStakingApi()
|
|
23
18
|
const assetName = 'ethereum'
|
|
19
|
+
const stakingProvider = stakingProviderClientFactory()
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
const { polygon: asset,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
})
|
|
21
|
+
async function getStakeAssets() {
|
|
22
|
+
const { polygon: asset, ethereum: feeAsset } = await assetClientInterface.getAssetsForNetwork({
|
|
23
|
+
baseAssetName: assetName,
|
|
24
|
+
})
|
|
30
25
|
return { asset, feeAsset }
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
const createStakingApi = memoize(async () => {
|
|
34
|
-
const { asset, feeAsset } = await getStakeAssets()
|
|
35
|
-
return { asset, feeAsset, stakingApi: createStakingApiForFeeAsset({ feeAsset }) }
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
// Helper function which selects the correct `asset` to use for when
|
|
39
|
-
// determining `feeData` for MATIC staking.
|
|
40
|
-
const resolveFeeData = async ({ feeData }) => {
|
|
41
|
-
const { feeAsset } = await getStakeAssets()
|
|
42
|
-
return defaultResolveFeeData({ asset: feeAsset, assetClientInterface, feeData })
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const getLatestFeeData = async () => {
|
|
46
|
-
const { feeAsset } = await getStakeAssets()
|
|
47
|
-
return assetClientInterface.getFeeData({ assetName: feeAsset.name })
|
|
48
26
|
}
|
|
49
27
|
|
|
50
|
-
|
|
51
|
-
// If the caller provides truthy `feeData`, we can continue
|
|
52
|
-
// as normal.
|
|
53
|
-
if (feeData) return feeData
|
|
54
|
-
|
|
55
|
-
// If the caller specifically omits `feeData`, then we
|
|
56
|
-
// provide a backup without warning. This is useful for
|
|
57
|
-
// calls with no expectations on the caller to provide
|
|
58
|
-
// `feeData`, i.e. one-shot transactions, or transactions
|
|
59
|
-
// which do not render a fee estimation.
|
|
60
|
-
return getLatestFeeData()
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const getDelegatorAddress = async ({ walletAccount }) => {
|
|
28
|
+
async function delegate({ walletAccount, amount } = {}) {
|
|
64
29
|
const address = await assetClientInterface.getReceiveAddress({
|
|
65
30
|
assetName,
|
|
66
31
|
walletAccount,
|
|
67
32
|
})
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async function delegate({ walletAccount, amount, feeData, waitForConfirmation = true } = {}) {
|
|
72
|
-
const [delegatorAddress, { asset, stakingApi }] = await Promise.all([
|
|
73
|
-
getDelegatorAddress({ walletAccount }),
|
|
74
|
-
createStakingApi(),
|
|
75
|
-
])
|
|
76
|
-
|
|
77
|
-
// NOTE: We do not provide a silent fallback for `feeData` omission
|
|
78
|
-
// for a delegation transaction, since `feeData` is expected
|
|
79
|
-
// to have been provided during `estimateDelegateTxFee`.
|
|
80
|
-
feeData = await resolveFeeData({ feeData })
|
|
33
|
+
const delegatorAddress = address.toLowerCase()
|
|
81
34
|
|
|
35
|
+
const { asset } = await getStakeAssets()
|
|
82
36
|
amount = amountToCurrency({ asset, amount })
|
|
83
37
|
|
|
84
38
|
const txApproveData = await stakingApi.approveStakeManager(amount)
|
|
85
|
-
let { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
})
|
|
39
|
+
let { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
40
|
+
delegatorAddress,
|
|
41
|
+
stakingApi.polygonContract.address,
|
|
42
|
+
txApproveData
|
|
43
|
+
)
|
|
91
44
|
await prepareAndSendTx({
|
|
92
45
|
walletAccount,
|
|
93
|
-
waitForConfirmation,
|
|
46
|
+
waitForConfirmation: true,
|
|
94
47
|
to: stakingApi.polygonContract.address,
|
|
95
48
|
txData: txApproveData,
|
|
96
49
|
gasPrice,
|
|
97
50
|
gasLimit,
|
|
98
51
|
tipGasPrice,
|
|
99
|
-
feeData,
|
|
100
52
|
})
|
|
101
53
|
|
|
102
|
-
// NOTE: Whenever we `waitForConfirmation`, we must recalculate
|
|
103
|
-
// the `feeData` as this may have changed since our last
|
|
104
|
-
// transaction was mined.
|
|
105
|
-
//
|
|
106
|
-
// In most cases, this shouldn't affect previous estimates,
|
|
107
|
-
// since the amount we aren't trying to stake using the
|
|
108
|
-
// base currency; however for users with marginal balances,
|
|
109
|
-
// an increase in `gasPrice` could result in transaction
|
|
110
|
-
// failure.
|
|
111
|
-
//
|
|
112
|
-
// HACK: This will invaldiate the original estimate in calculated
|
|
113
|
-
// in `estimateDelegateTxFee`!
|
|
114
|
-
// TODO: Submit these transactions as an atomic bundle.
|
|
115
|
-
if (waitForConfirmation) {
|
|
116
|
-
feeData = await getLatestFeeData()
|
|
117
|
-
}
|
|
118
|
-
|
|
119
54
|
const txDelegateData = await stakingApi.delegate({ amount })
|
|
120
55
|
|
|
121
|
-
;({ gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}))
|
|
56
|
+
;({ gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
57
|
+
delegatorAddress,
|
|
58
|
+
stakingApi.validatorShareContract.address,
|
|
59
|
+
txDelegateData
|
|
60
|
+
))
|
|
127
61
|
|
|
128
62
|
const txId = await prepareAndSendTx({
|
|
129
63
|
walletAccount,
|
|
130
|
-
// TOOD: Why shouldn't we wait for confirmation here? Is it because we want to `notifyStaking`?
|
|
131
|
-
waitForConfirmation: false,
|
|
132
64
|
to: stakingApi.validatorShareContract.address,
|
|
133
65
|
txData: txDelegateData,
|
|
134
66
|
gasPrice,
|
|
135
67
|
gasLimit,
|
|
136
68
|
tipGasPrice,
|
|
137
|
-
feeData,
|
|
138
69
|
})
|
|
139
70
|
|
|
140
71
|
await stakingProvider.notifyStaking({
|
|
@@ -147,24 +78,22 @@ export function createPolygonStakingService({
|
|
|
147
78
|
return txId
|
|
148
79
|
}
|
|
149
80
|
|
|
150
|
-
async function undelegate({ walletAccount, amount
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
])
|
|
81
|
+
async function undelegate({ walletAccount, amount } = {}) {
|
|
82
|
+
const address = await assetClientInterface.getReceiveAddress({
|
|
83
|
+
assetName,
|
|
84
|
+
walletAccount,
|
|
85
|
+
})
|
|
86
|
+
const delegatorAddress = address.toLowerCase()
|
|
157
87
|
|
|
88
|
+
const { asset } = await getStakeAssets()
|
|
158
89
|
amount = amountToCurrency({ asset, amount })
|
|
159
90
|
|
|
160
91
|
const txUndelegateData = await stakingApi.undelegate({ amount })
|
|
161
|
-
const { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
})
|
|
167
|
-
|
|
92
|
+
const { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
93
|
+
delegatorAddress.toLowerCase(),
|
|
94
|
+
stakingApi.validatorShareContract.address,
|
|
95
|
+
txUndelegateData
|
|
96
|
+
)
|
|
168
97
|
return prepareAndSendTx({
|
|
169
98
|
walletAccount,
|
|
170
99
|
to: stakingApi.validatorShareContract.address,
|
|
@@ -172,26 +101,22 @@ export function createPolygonStakingService({
|
|
|
172
101
|
gasPrice,
|
|
173
102
|
gasLimit,
|
|
174
103
|
tipGasPrice,
|
|
175
|
-
feeData,
|
|
176
|
-
waitForConfirmation,
|
|
177
104
|
})
|
|
178
105
|
}
|
|
179
106
|
|
|
180
|
-
async function claimRewards({ walletAccount
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
])
|
|
107
|
+
async function claimRewards({ walletAccount } = {}) {
|
|
108
|
+
const address = await assetClientInterface.getReceiveAddress({
|
|
109
|
+
assetName,
|
|
110
|
+
walletAccount,
|
|
111
|
+
})
|
|
112
|
+
const delegatorAddress = address.toLowerCase()
|
|
187
113
|
|
|
188
114
|
const txWithdrawRewardsData = await stakingApi.withdrawRewards()
|
|
189
|
-
const { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
})
|
|
115
|
+
const { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
116
|
+
delegatorAddress,
|
|
117
|
+
stakingApi.validatorShareContract.address,
|
|
118
|
+
txWithdrawRewardsData
|
|
119
|
+
)
|
|
195
120
|
return prepareAndSendTx({
|
|
196
121
|
walletAccount,
|
|
197
122
|
to: stakingApi.validatorShareContract.address,
|
|
@@ -199,18 +124,17 @@ export function createPolygonStakingService({
|
|
|
199
124
|
gasPrice,
|
|
200
125
|
gasLimit,
|
|
201
126
|
tipGasPrice,
|
|
202
|
-
feeData,
|
|
203
127
|
})
|
|
204
128
|
}
|
|
205
129
|
|
|
206
|
-
async function claimUndelegatedBalance({ walletAccount, unbondNonce
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
])
|
|
130
|
+
async function claimUndelegatedBalance({ walletAccount, unbondNonce } = {}) {
|
|
131
|
+
const address = await assetClientInterface.getReceiveAddress({
|
|
132
|
+
assetName,
|
|
133
|
+
walletAccount,
|
|
134
|
+
})
|
|
135
|
+
const delegatorAddress = address.toLowerCase()
|
|
213
136
|
|
|
137
|
+
const { asset } = await getStakeAssets()
|
|
214
138
|
const { currency } = asset
|
|
215
139
|
const unstakedClaimInfo = await fetchUnstakedClaimInfo({
|
|
216
140
|
stakingApi,
|
|
@@ -225,12 +149,11 @@ export function createPolygonStakingService({
|
|
|
225
149
|
})
|
|
226
150
|
|
|
227
151
|
const txClaimUndelegatedData = await stakingApi.claimUndelegatedBalance({ unbondNonce })
|
|
228
|
-
const { gasPrice, gasLimit, tipGasPrice } = await estimateTxFee(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
})
|
|
152
|
+
const { gasPrice, gasLimit, tipGasPrice, fee } = await estimateTxFee(
|
|
153
|
+
delegatorAddress,
|
|
154
|
+
stakingApi.validatorShareContract.address,
|
|
155
|
+
txClaimUndelegatedData
|
|
156
|
+
)
|
|
234
157
|
const txId = await prepareAndSendTx({
|
|
235
158
|
walletAccount,
|
|
236
159
|
to: stakingApi.validatorShareContract.address,
|
|
@@ -238,7 +161,7 @@ export function createPolygonStakingService({
|
|
|
238
161
|
gasPrice,
|
|
239
162
|
gasLimit,
|
|
240
163
|
tipGasPrice,
|
|
241
|
-
|
|
164
|
+
fee,
|
|
242
165
|
})
|
|
243
166
|
|
|
244
167
|
await stakingProvider.notifyUnstaking({
|
|
@@ -251,42 +174,31 @@ export function createPolygonStakingService({
|
|
|
251
174
|
return txId
|
|
252
175
|
}
|
|
253
176
|
|
|
254
|
-
async function estimateDelegateOperation({
|
|
255
|
-
walletAccount,
|
|
256
|
-
operation,
|
|
257
|
-
args,
|
|
258
|
-
// NOTE: When estimating transactions, ideally we'd expect the `feeData`
|
|
259
|
-
// that we intend to send the transaction using. If this is not
|
|
260
|
-
// defined, we'll fallback to a default (with a warning).
|
|
261
|
-
feeData,
|
|
262
|
-
}) {
|
|
263
|
-
// HACK: For delegation transactions, we must fall back to the
|
|
264
|
-
// custom implementation, since we can't currently estimate
|
|
265
|
-
// the transaction due to a dependence upon approvals.
|
|
266
|
-
if (operation === 'delegate') return estimateDelegateTxFee({ feeData })
|
|
267
|
-
|
|
268
|
-
const [delegatorAddress, { asset, stakingApi }] = await Promise.all([
|
|
269
|
-
getDelegatorAddress({ walletAccount }),
|
|
270
|
-
createStakingApi(),
|
|
271
|
-
])
|
|
272
|
-
|
|
273
|
-
feeData = await resolveFeeData({ feeData })
|
|
274
|
-
|
|
177
|
+
async function estimateDelegateOperation({ walletAccount, operation, args }) {
|
|
275
178
|
const delegateOperation = stakingApi[operation]
|
|
276
|
-
|
|
179
|
+
|
|
180
|
+
if (!delegateOperation) {
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const address = await assetClientInterface.getReceiveAddress({
|
|
185
|
+
assetName,
|
|
186
|
+
walletAccount,
|
|
187
|
+
})
|
|
188
|
+
const delegatorAddress = address.toLowerCase()
|
|
277
189
|
|
|
278
190
|
const { amount } = args
|
|
279
191
|
if (amount) {
|
|
192
|
+
const { asset } = await getStakeAssets()
|
|
280
193
|
args = { ...args, amount: amountToCurrency({ asset, amount }) }
|
|
281
194
|
}
|
|
282
195
|
|
|
283
196
|
const operationTxData = await delegateOperation({ ...args, walletAccount })
|
|
284
|
-
const { fee } = await estimateTxFee(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
})
|
|
197
|
+
const { fee } = await estimateTxFee(
|
|
198
|
+
delegatorAddress,
|
|
199
|
+
stakingApi.validatorShareContract.address,
|
|
200
|
+
operationTxData
|
|
201
|
+
)
|
|
290
202
|
|
|
291
203
|
return fee
|
|
292
204
|
}
|
|
@@ -301,27 +213,23 @@ export function createPolygonStakingService({
|
|
|
301
213
|
* This is just for displaying purposes and it's just an aproximation of the delegate gas cost,
|
|
302
214
|
* NOT the real fee cost
|
|
303
215
|
*/
|
|
304
|
-
async function estimateDelegateTxFee(
|
|
216
|
+
async function estimateDelegateTxFee() {
|
|
305
217
|
// approx gas limits
|
|
306
218
|
const { ethereum } = await assetClientInterface.getAssetsForNetwork({
|
|
307
219
|
baseAssetName: 'ethereum',
|
|
308
220
|
})
|
|
221
|
+
const erc20ApproveGas = 4900
|
|
222
|
+
const delegateGas = 240_000
|
|
223
|
+
const gasPrice = parseInt(await getServer(ethereum).gasPrice(), 16)
|
|
224
|
+
const extraPercentage = 20
|
|
225
|
+
|
|
226
|
+
const gasLimit = erc20ApproveGas + delegateGas
|
|
227
|
+
const gasLimitWithBuffer = new BN(gasLimit)
|
|
228
|
+
.imuln(100 + extraPercentage)
|
|
229
|
+
.idivn(100)
|
|
230
|
+
.toString()
|
|
309
231
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
// TODO: update estimation to use a mock source address for
|
|
313
|
-
// deposits so we can simulate the necessary approvals,
|
|
314
|
-
// we shouldn't maintain constants like these
|
|
315
|
-
const erc20ApproveGas = 80_000
|
|
316
|
-
const delegateGas = 250_000
|
|
317
|
-
|
|
318
|
-
const { gasPrice } = getFeeFactoryGasPrices({ feeData })
|
|
319
|
-
|
|
320
|
-
const gasLimitWithBuffer = scaleGasLimitEstimate({
|
|
321
|
-
estimatedGasLimit: BigInt(erc20ApproveGas + delegateGas),
|
|
322
|
-
})
|
|
323
|
-
|
|
324
|
-
const fee = BigInt(gasLimitWithBuffer) * BigInt(gasPrice.toBaseNumber())
|
|
232
|
+
const fee = new BN(gasLimitWithBuffer).mul(new BN(gasPrice))
|
|
325
233
|
|
|
326
234
|
return {
|
|
327
235
|
gasLimit: gasLimitWithBuffer,
|
|
@@ -330,7 +238,7 @@ export function createPolygonStakingService({
|
|
|
330
238
|
}
|
|
331
239
|
}
|
|
332
240
|
|
|
333
|
-
async function estimateTxFee(
|
|
241
|
+
async function estimateTxFee(from, to, txInput) {
|
|
334
242
|
const { ethereum } = await assetClientInterface.getAssetsForNetwork({
|
|
335
243
|
baseAssetName: 'ethereum',
|
|
336
244
|
})
|
|
@@ -346,7 +254,13 @@ export function createPolygonStakingService({
|
|
|
346
254
|
DISABLE_BALANCE_CHECKS
|
|
347
255
|
)
|
|
348
256
|
|
|
349
|
-
return
|
|
257
|
+
return getEvmStakingServiceFee({
|
|
258
|
+
amount,
|
|
259
|
+
asset: ethereum,
|
|
260
|
+
assetClientInterface,
|
|
261
|
+
maxPriorityFeePerGas: MAX_PRIORITY_FEE_PER_GAS,
|
|
262
|
+
gasLimit,
|
|
263
|
+
})
|
|
350
264
|
}
|
|
351
265
|
|
|
352
266
|
async function prepareAndSendTx({
|
|
@@ -357,7 +271,6 @@ export function createPolygonStakingService({
|
|
|
357
271
|
gasLimit,
|
|
358
272
|
tipGasPrice,
|
|
359
273
|
waitForConfirmation = false,
|
|
360
|
-
feeData,
|
|
361
274
|
} = {}) {
|
|
362
275
|
const { ethereum: asset } = await assetClientInterface.getAssetsForNetwork({
|
|
363
276
|
baseAssetName: 'ethereum',
|
|
@@ -375,7 +288,6 @@ export function createPolygonStakingService({
|
|
|
375
288
|
tipGasPrice,
|
|
376
289
|
},
|
|
377
290
|
waitForConfirmation,
|
|
378
|
-
feeData,
|
|
379
291
|
}
|
|
380
292
|
|
|
381
293
|
const { txId } = await asset.baseAsset.api.sendTx(sendTxArgs)
|
|
@@ -497,8 +409,9 @@ async function fetchRewardsInfo({ stakingApi, delegator, currency }) {
|
|
|
497
409
|
}
|
|
498
410
|
}
|
|
499
411
|
|
|
500
|
-
export async function getPolygonStakingInfo({ address, asset
|
|
501
|
-
const
|
|
412
|
+
export async function getPolygonStakingInfo({ address, asset, server }) {
|
|
413
|
+
const { currency } = asset
|
|
414
|
+
const stakingApi = new MaticStakingApi(undefined, undefined, server)
|
|
502
415
|
const delegator = address.toLowerCase()
|
|
503
416
|
const [
|
|
504
417
|
delegatedBalance,
|
|
@@ -1,39 +1,73 @@
|
|
|
1
1
|
import { isNumberUnit } from '@exodus/currency'
|
|
2
2
|
|
|
3
|
-
export const DISABLE_BALANCE_CHECKS = '0x0'
|
|
4
|
-
|
|
5
3
|
export function amountToCurrency({ asset, amount }) {
|
|
6
4
|
return isNumberUnit(amount) ? amount : asset.currency.parse(amount)
|
|
7
5
|
}
|
|
8
6
|
|
|
7
|
+
// HACK: Empirically, we can observe that a `feeData` object uses
|
|
8
|
+
// stringified values for gas i.e. `baseFeePerGas: "10 gwei"`,
|
|
9
|
+
// however to hook into `asset.api.getFees()`, these values
|
|
10
|
+
// must be expressed using currency objects.
|
|
11
|
+
function maybeNormalizeFeeData({ asset, feeData }) {
|
|
12
|
+
const { baseFeePerGas, tipGasPrice, serverGasPrice, gasPrice, ...extras } = feeData
|
|
13
|
+
|
|
14
|
+
const maybeNormalizeAmount = (amount) =>
|
|
15
|
+
typeof amount === 'string' ? amountToCurrency({ amount, asset }) : amount
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
...extras,
|
|
19
|
+
gasPrice: maybeNormalizeAmount(gasPrice),
|
|
20
|
+
baseFeePerGas: maybeNormalizeAmount(baseFeePerGas),
|
|
21
|
+
tipGasPrice: maybeNormalizeAmount(tipGasPrice),
|
|
22
|
+
serverGasPrice: maybeNormalizeAmount(serverGasPrice),
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
9
26
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* For example, when determining the amount for a `sendAll`
|
|
16
|
-
* transaction, if the gas price were to increase between estimation
|
|
17
|
-
* and execution, this would invalidate the previous estimation.
|
|
18
|
-
*
|
|
19
|
-
* In future usage, we would want assets code to fetch FeeData from ACI,
|
|
20
|
-
* construct a transaction including determining the fee and then report
|
|
21
|
-
* back to the client what the fee is going to be.
|
|
22
|
-
*
|
|
23
|
-
* Importantly, the provided `feeData` must be sufficiently
|
|
24
|
-
* up-to-date; else, we risk the transactions not being included at all.
|
|
27
|
+
* A common handler for the computation of the `fee`, `gasPrice`,
|
|
28
|
+
* `gasLimit` and `tipGasPrice` or a staking call. Allows the caller
|
|
29
|
+
* to specify a custom `maxPriorityFeePerGas` whilst the current
|
|
30
|
+
* `tipGasPrice` is incompatible with EIP-1559 pricing.
|
|
25
31
|
*/
|
|
26
|
-
export
|
|
27
|
-
|
|
32
|
+
export async function getEvmStakingServiceFee({
|
|
33
|
+
amount,
|
|
28
34
|
asset,
|
|
29
35
|
assetClientInterface,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
maxPriorityFeePerGas: maybeMaxPriorityFeePerGas,
|
|
37
|
+
gasLimit,
|
|
38
|
+
}) {
|
|
39
|
+
const defaultFeeData = await assetClientInterface.getFeeData({
|
|
40
|
+
assetName: asset.name,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
// HACK: We wish to explicitly enable `useBaseGasPrice`
|
|
44
|
+
// since we're using a custom `tipGasPrice`. This
|
|
45
|
+
// will be compatible during the transition from
|
|
46
|
+
// Magnifier to Clarity, and can be safely removed
|
|
47
|
+
// once `useBaseGasPrice` is the default behaviour
|
|
48
|
+
// (alongside the custom tip).
|
|
49
|
+
const { eip1559Enabled } = defaultFeeData
|
|
50
|
+
const useBaseGasPrice = Boolean(eip1559Enabled) && Boolean(maybeMaxPriorityFeePerGas)
|
|
33
51
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
52
|
+
const normalizedFeeData = maybeNormalizeFeeData({
|
|
53
|
+
asset,
|
|
54
|
+
feeData: {
|
|
55
|
+
...defaultFeeData,
|
|
56
|
+
// HACK: The backend currently exports a very large `tipGasPrice` that is
|
|
57
|
+
// compatible with Magnifier's legacy `gasPrice`, but it would
|
|
58
|
+
// be incompatible with EIP-1559, since this would evaluate into
|
|
59
|
+
// a very large `maxPriorityFeePerGas`.
|
|
60
|
+
...(useBaseGasPrice ? { tipGasPrice: maybeMaxPriorityFeePerGas } : null),
|
|
61
|
+
useBaseGasPrice,
|
|
62
|
+
},
|
|
63
|
+
})
|
|
37
64
|
|
|
38
|
-
|
|
65
|
+
// Returns `fee`, `gasPrice`, `extraFeeData`, and `tipGasPrice`
|
|
66
|
+
// if the config defines we should `useBaseGasPrice`.
|
|
67
|
+
return asset.api.getFee({
|
|
68
|
+
asset,
|
|
69
|
+
feeData: normalizedFeeData,
|
|
70
|
+
gasLimit,
|
|
71
|
+
amount,
|
|
72
|
+
})
|
|
39
73
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { ensureSaneEip1559GasPriceForTipGasPrice } from '../fee-utils.js'
|
|
2
1
|
import { fetchGasLimit } from '../gas-estimation.js'
|
|
3
|
-
import { getFeeFactoryGasPrices } from '../get-fee.js'
|
|
4
2
|
|
|
5
3
|
const getFeeInfo = async function getFeeInfo({
|
|
6
4
|
assetClientInterface,
|
|
@@ -10,48 +8,25 @@ const getFeeInfo = async function getFeeInfo({
|
|
|
10
8
|
amount,
|
|
11
9
|
txInput,
|
|
12
10
|
feeOpts = {},
|
|
13
|
-
feeData,
|
|
14
|
-
customFee,
|
|
15
11
|
}) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// NOTE: This shouldn't actually be used outside of the `assets` repo;
|
|
20
|
-
// this is done just for safety.
|
|
21
|
-
if (!feeData) {
|
|
22
|
-
console.warn('`getFeeInfo` was not explicitly passed a `feeData` object.')
|
|
23
|
-
const { name: assetName } = asset
|
|
24
|
-
feeData = await assetClientInterface.getFeeData({ assetName })
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const {
|
|
28
|
-
gasPrice: gasPrice_,
|
|
29
|
-
feeData: { tipGasPrice: tipGasPrice_, eip1559Enabled },
|
|
30
|
-
} = getFeeFactoryGasPrices({ customFee, feeData })
|
|
31
|
-
|
|
32
|
-
const tipGasPrice = feeOpts.tipGasPrice || tipGasPrice_
|
|
12
|
+
const { gasPrice: gasPrice_, tipGasPrice: tipGasPrice_ } = await assetClientInterface.getFeeData({
|
|
13
|
+
assetName: asset.name,
|
|
14
|
+
})
|
|
33
15
|
|
|
34
|
-
|
|
16
|
+
let { gasLimit, gasPrice = gasPrice_, tipGasPrice = tipGasPrice_ } = feeOpts
|
|
35
17
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
// we must normalize these before returning.
|
|
39
|
-
const gasPrice = eip1559Enabled
|
|
40
|
-
? ensureSaneEip1559GasPriceForTipGasPrice({ gasPrice: maybeGasPrice, tipGasPrice })
|
|
41
|
-
: maybeGasPrice
|
|
42
|
-
|
|
43
|
-
const gasLimit =
|
|
44
|
-
feeOpts.gasLimit ||
|
|
45
|
-
(await fetchGasLimit({
|
|
18
|
+
if (!gasLimit) {
|
|
19
|
+
gasLimit = await fetchGasLimit({
|
|
46
20
|
asset,
|
|
47
21
|
fromAddress,
|
|
48
22
|
toAddress,
|
|
49
23
|
amount,
|
|
50
24
|
txInput,
|
|
51
25
|
throwOnError: false,
|
|
52
|
-
})
|
|
26
|
+
})
|
|
27
|
+
}
|
|
53
28
|
|
|
54
|
-
return { gasPrice, gasLimit, tipGasPrice
|
|
29
|
+
return { gasPrice, gasLimit, tipGasPrice }
|
|
55
30
|
}
|
|
56
31
|
|
|
57
32
|
export default getFeeInfo
|
|
@@ -7,11 +7,10 @@ export const resolveNonce = async ({
|
|
|
7
7
|
providedNonce,
|
|
8
8
|
txLog,
|
|
9
9
|
triedNonce,
|
|
10
|
-
tag = 'latest', // use 'pending' for unconfirmed txs
|
|
11
10
|
}) => {
|
|
12
11
|
const nonceFromNode =
|
|
13
12
|
asset.baseAsset?.api?.features?.noHistory || forceFromNode
|
|
14
|
-
? await getNonce({ asset: asset.baseAsset, address: fromAddress, tag })
|
|
13
|
+
? await getNonce({ asset: asset.baseAsset, address: fromAddress, tag: 'latest' }) // maybe 'pending' to unconfirmed txs
|
|
15
14
|
: 0
|
|
16
15
|
|
|
17
16
|
const nonceFromLog = [...txLog]
|