@exodus/ethereum-api 6.3.15 → 6.3.16
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/package.json +2 -2
- package/src/staking/{ethereum-staking.js → ethereum/api.js} +1 -1
- package/src/staking/ethereum/index.js +3 -0
- package/src/staking/ethereum/service.js +267 -0
- package/src/staking/{ethereum-staking-utils.js → ethereum/staking-utils.js} +1 -1
- package/src/staking/index.js +1 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "6.3.
|
|
3
|
+
"version": "6.3.16",
|
|
4
4
|
"description": "Ethereum Api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@exodus/models": "^8.10.4"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "4a98199d02033b9d822b2065f3725a3bd4a1b83d"
|
|
38
38
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createContract } from '@exodus/ethereum-lib'
|
|
2
|
-
import { getServerByName } from '
|
|
2
|
+
import { getServerByName } from '../../exodus-eth-server'
|
|
3
3
|
import { retry } from '@exodus/simple-retry'
|
|
4
4
|
import { bufferToHex } from '@exodus/ethereumjs-util'
|
|
5
5
|
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { estimateGasLimit, EthereumStaking, getServer } from '@exodus/ethereum-api'
|
|
2
|
+
import BN from 'bn.js'
|
|
3
|
+
|
|
4
|
+
const delegateGas = 500000 // approx gas limits
|
|
5
|
+
|
|
6
|
+
export function createEthereumStakingService({
|
|
7
|
+
asset,
|
|
8
|
+
assetClientInterface,
|
|
9
|
+
createAndBroadcastTX,
|
|
10
|
+
}) {
|
|
11
|
+
const staking = new EthereumStaking(asset)
|
|
12
|
+
|
|
13
|
+
async function delegate({ walletAccount, delegatorAddress, amount } = {}) {
|
|
14
|
+
const _delegatorAddress = delegatorAddress.toLowerCase()
|
|
15
|
+
|
|
16
|
+
const { to, data } = await staking.stake({
|
|
17
|
+
amount,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const { gasPrice, gasLimit, fee } = await estimateTxFee(_delegatorAddress, to, amount, data)
|
|
21
|
+
|
|
22
|
+
console.log(
|
|
23
|
+
`delegator address ${delegatorAddress} staking ${amount.toDefaultString({
|
|
24
|
+
unit: true,
|
|
25
|
+
})}`
|
|
26
|
+
)
|
|
27
|
+
const txId = await prepareAndSendTx({
|
|
28
|
+
asset,
|
|
29
|
+
walletAccount,
|
|
30
|
+
to,
|
|
31
|
+
amount,
|
|
32
|
+
txData: data,
|
|
33
|
+
gasPrice,
|
|
34
|
+
gasLimit,
|
|
35
|
+
fee,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return txId
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function undelegate({ walletAccount, delegatorAddress, amount } = {}) {
|
|
42
|
+
/*
|
|
43
|
+
unstakePending balance (not yet in validator) + unstake balance (in validator)
|
|
44
|
+
1. give priority to unstakePending (based on the amount)
|
|
45
|
+
2. unstake amount in validator.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
const pendingAmount = await staking.pendingBalanceOf(delegatorAddress)
|
|
49
|
+
|
|
50
|
+
console.log(
|
|
51
|
+
`delegator address ${delegatorAddress} unstaking ${amount.toDefaultString({
|
|
52
|
+
unit: true,
|
|
53
|
+
})} - pending amount: ${pendingAmount.toDefaultString({ unit: true })}`
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
let txId
|
|
57
|
+
if (!pendingAmount.isZero) {
|
|
58
|
+
const { to, data } = await staking.unstakePending({
|
|
59
|
+
address: delegatorAddress,
|
|
60
|
+
amount,
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
let { gasPrice, gasLimit, fee } = await estimateTxFee(
|
|
64
|
+
delegatorAddress.toLowerCase(),
|
|
65
|
+
to,
|
|
66
|
+
null,
|
|
67
|
+
data
|
|
68
|
+
)
|
|
69
|
+
txId = await prepareAndSendTx({
|
|
70
|
+
asset,
|
|
71
|
+
walletAccount,
|
|
72
|
+
to: to,
|
|
73
|
+
txData: data,
|
|
74
|
+
gasPrice,
|
|
75
|
+
gasLimit,
|
|
76
|
+
fee,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// need also to unstake
|
|
81
|
+
if (amount.gt(pendingAmount)) {
|
|
82
|
+
const amountToUnstake = amount.sub(pendingAmount)
|
|
83
|
+
const { to, data } = await staking.unstake({
|
|
84
|
+
address: delegatorAddress,
|
|
85
|
+
amount: amountToUnstake,
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
let { gasPrice, gasLimit, fee } = await estimateTxFee(delegatorAddress, to, null, data)
|
|
89
|
+
txId = await prepareAndSendTx({
|
|
90
|
+
asset,
|
|
91
|
+
walletAccount,
|
|
92
|
+
to,
|
|
93
|
+
txData: data,
|
|
94
|
+
gasPrice,
|
|
95
|
+
gasLimit,
|
|
96
|
+
fee,
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return txId
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function claimUndelegatedBalance({ walletAccount, delegatorAddress } = {}) {
|
|
104
|
+
// withdraw withdrawable balance (of a previous unstake)
|
|
105
|
+
|
|
106
|
+
const withdrawRequest = await staking.claimWithdrawRequest({
|
|
107
|
+
address: delegatorAddress,
|
|
108
|
+
})
|
|
109
|
+
if (withdrawRequest) {
|
|
110
|
+
const { to, data } = withdrawRequest
|
|
111
|
+
let { gasPrice, gasLimit, fee } = await estimateTxFee(delegatorAddress, to, null, data)
|
|
112
|
+
const txId = await prepareAndSendTx({
|
|
113
|
+
asset,
|
|
114
|
+
walletAccount,
|
|
115
|
+
to: to,
|
|
116
|
+
txData: data,
|
|
117
|
+
gasPrice,
|
|
118
|
+
gasLimit,
|
|
119
|
+
fee,
|
|
120
|
+
})
|
|
121
|
+
return txId
|
|
122
|
+
} else return null // -> no withdrawable balance
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function estimateDelegateOperation({ delegatorAddress, operation, args }) {
|
|
126
|
+
const NAMING_MAP = {
|
|
127
|
+
delegate: 'stake',
|
|
128
|
+
undelegate: 'unstake',
|
|
129
|
+
claimUndelegatedBalance: 'claimWithdrawRequest',
|
|
130
|
+
}
|
|
131
|
+
const delegateOperation = staking[NAMING_MAP[operation]].bind(staking)
|
|
132
|
+
|
|
133
|
+
if (!delegateOperation) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const { amount, data } = await delegateOperation(args)
|
|
138
|
+
const { fee } = await estimateTxFee(delegatorAddress, staking.poolAddress, amount, data)
|
|
139
|
+
|
|
140
|
+
return fee
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function estimateUndelegateTxFee() {
|
|
144
|
+
const gasPrice = parseInt(await getServer(asset).gasPrice(), 16)
|
|
145
|
+
|
|
146
|
+
const gasLimit = delegateGas
|
|
147
|
+
const fee = new BN(gasLimit).mul(new BN(gasPrice))
|
|
148
|
+
return {
|
|
149
|
+
gasLimit,
|
|
150
|
+
gasPrice: asset.currency.baseUnit(gasPrice),
|
|
151
|
+
fee: asset.currency.baseUnit(fee.toString()),
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function estimateTxFee(from, to, amount, txInput, gasPrice = '0x0') {
|
|
156
|
+
amount = amount || asset.currency.ZERO
|
|
157
|
+
if (amount.isZero) {
|
|
158
|
+
// unstaking txs
|
|
159
|
+
return estimateUndelegateTxFee() // otherwise server estimateGasLimit will throw "execution reverted"
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const gasLimit = await estimateGasLimit(
|
|
163
|
+
asset,
|
|
164
|
+
from.toLowerCase(),
|
|
165
|
+
to.toLowerCase(),
|
|
166
|
+
amount, // staking contracts does not always require ETH amount to interact with
|
|
167
|
+
txInput,
|
|
168
|
+
gasPrice
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
if (gasPrice === '0x0') {
|
|
172
|
+
gasPrice = await getServer(asset).gasPrice()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
gasPrice = parseInt(gasPrice, 16)
|
|
176
|
+
const fee = new BN(gasPrice).mul(new BN(gasLimit))
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
gasLimit,
|
|
180
|
+
gasPrice: asset.currency.baseUnit(gasPrice),
|
|
181
|
+
fee: asset.currency.baseUnit(fee.toString()),
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function getMinAmount() {
|
|
186
|
+
return staking.minAmount
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function prepareAndSendTx({
|
|
190
|
+
asset,
|
|
191
|
+
walletAccount,
|
|
192
|
+
to,
|
|
193
|
+
amount,
|
|
194
|
+
txData: txInput,
|
|
195
|
+
gasPrice,
|
|
196
|
+
gasLimit,
|
|
197
|
+
fee,
|
|
198
|
+
} = {}) {
|
|
199
|
+
const sendTxArgs = {
|
|
200
|
+
asset,
|
|
201
|
+
walletAccount,
|
|
202
|
+
address: to,
|
|
203
|
+
amount: amount || asset.currency.ZERO,
|
|
204
|
+
options: {
|
|
205
|
+
shouldLog: true,
|
|
206
|
+
txInput,
|
|
207
|
+
gasPrice,
|
|
208
|
+
gasLimit,
|
|
209
|
+
feeAmount: fee,
|
|
210
|
+
},
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log('sending staking tx:', sendTxArgs)
|
|
214
|
+
|
|
215
|
+
const { txId } = await createAndBroadcastTX(sendTxArgs)
|
|
216
|
+
|
|
217
|
+
return txId
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
delegate,
|
|
222
|
+
undelegate,
|
|
223
|
+
claimUndelegatedBalance,
|
|
224
|
+
getEthereumStakingInfo,
|
|
225
|
+
estimateDelegateOperation,
|
|
226
|
+
getMinAmount,
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export async function getEthereumStakingInfo({ address, asset }) {
|
|
231
|
+
const { currency } = asset
|
|
232
|
+
const delegator = address.toLowerCase()
|
|
233
|
+
const staking = new EthereumStaking(asset)
|
|
234
|
+
|
|
235
|
+
const [activeStakedBalance, pendingBalance, withdrawRequest, rewardsBalance] = await Promise.all([
|
|
236
|
+
staking.autocompoundBalanceOf(delegator),
|
|
237
|
+
staking.pendingBalanceOf(delegator),
|
|
238
|
+
staking.withdrawRequest(delegator),
|
|
239
|
+
staking.getLiquidRewards(delegator),
|
|
240
|
+
])
|
|
241
|
+
|
|
242
|
+
const delegatedBalance = activeStakedBalance.add(pendingBalance)
|
|
243
|
+
const minDelegateAmount = currency.defaultUnit(0.1)
|
|
244
|
+
|
|
245
|
+
const {
|
|
246
|
+
requested,
|
|
247
|
+
canSendClaimRequest,
|
|
248
|
+
/*, readyForClaim */
|
|
249
|
+
} = withdrawRequest
|
|
250
|
+
|
|
251
|
+
const isDelegating = !delegatedBalance.isZero || !requested.isZero
|
|
252
|
+
const isUndelegateInProgress = !requested.isZero && !canSendClaimRequest
|
|
253
|
+
|
|
254
|
+
const canClaimUndelegatedBalance = canSendClaimRequest
|
|
255
|
+
|
|
256
|
+
const unclaimedUndelegatedBalance = requested
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
rewardsBalance,
|
|
260
|
+
isDelegating,
|
|
261
|
+
delegatedBalance,
|
|
262
|
+
minDelegateAmount,
|
|
263
|
+
unclaimedUndelegatedBalance,
|
|
264
|
+
canClaimUndelegatedBalance,
|
|
265
|
+
isUndelegateInProgress,
|
|
266
|
+
}
|
|
267
|
+
}
|
package/src/staking/index.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
export { EthereumStaking } from './ethereum-staking'
|
|
2
1
|
export { MaticStaking } from './matic-staking'
|
|
3
2
|
export { FantomStaking } from './fantom-staking'
|
|
4
3
|
export { stakingProviderClientFactory } from './staking-provider-client'
|
|
5
4
|
export * from './matic-staking-utils'
|
|
6
|
-
export * from './ethereum
|
|
5
|
+
export * from './ethereum'
|