@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/ethereum-api",
3
- "version": "6.3.15",
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": "f3e3f031b8de37e0540523227469c0991d90c8b3"
37
+ "gitHead": "4a98199d02033b9d822b2065f3725a3bd4a1b83d"
38
38
  }
@@ -1,5 +1,5 @@
1
1
  import { createContract } from '@exodus/ethereum-lib'
2
- import { getServerByName } from '../exodus-eth-server'
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,3 @@
1
+ export * from './staking-utils'
2
+ export * from './api'
3
+ export * from './service'
@@ -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
+ }
@@ -1,4 +1,4 @@
1
- import { EthereumStaking } from './ethereum-staking'
1
+ import { EthereumStaking } from './api'
2
2
 
3
3
  const { DELEGATE, UNSTAKE, UNSTAKE_PENDING } = EthereumStaking.METHODS_IDS
4
4
 
@@ -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-staking-utils'
5
+ export * from './ethereum'