@exodus/ethereum-api 6.3.35 → 7.0.1
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 +8 -5
- package/src/staking/ethereum/service.js +38 -6
- package/src/staking/index.js +1 -2
- package/src/staking/{matic-staking.js → matic/api.js} +6 -7
- package/src/staking/matic/index.js +9 -0
- package/src/staking/{matic-staking-utils.js → matic/matic-staking-utils.js} +5 -12
- package/src/staking/matic/service.js +442 -0
- package/src/tx-log/clarity-utils/get-log-items-from-server-tx.js +8 -0
- package/src/tx-log/monitor-utils/get-log-items-from-server-tx.js +8 -0
- package/LICENSE.md +0 -0
- /package/src/staking/{matic-staking.md → matic/matic-staking.md} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.1",
|
|
4
4
|
"description": "Ethereum Api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@exodus/asset-lib": "^3.7.1",
|
|
18
18
|
"@exodus/crypto": "^1.0.0-rc.0",
|
|
19
|
+
"@exodus/currency": "^2.1.3",
|
|
19
20
|
"@exodus/ethereum-lib": "^4.0.3",
|
|
20
21
|
"@exodus/ethereumjs-util": "^7.1.0-exodus.6",
|
|
21
22
|
"@exodus/fetch": "^1.3.0-beta.4",
|
|
@@ -28,11 +29,13 @@
|
|
|
28
29
|
"make-concurrent": "4.0.0",
|
|
29
30
|
"minimalistic-assert": "^1.0.1",
|
|
30
31
|
"ms": "^2.1.1",
|
|
31
|
-
"socket.io-client": "2.
|
|
32
|
+
"socket.io-client": "2.1.1",
|
|
32
33
|
"ws": "^6.1.0"
|
|
33
34
|
},
|
|
34
35
|
"devDependencies": {
|
|
35
|
-
"@exodus/
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
"@exodus/assets": "9.1.0",
|
|
37
|
+
"@exodus/assets-base": "^8.1.14",
|
|
38
|
+
"@exodus/assets-testing": "file:../../../__testing__",
|
|
39
|
+
"@exodus/models": "^11.0.0"
|
|
40
|
+
}
|
|
38
41
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { estimateGasLimit, EthereumStaking, getServer } from '@exodus/ethereum-api'
|
|
2
|
+
import { isNumberUnit } from '@exodus/currency'
|
|
2
3
|
import BN from 'bn.js'
|
|
3
4
|
|
|
4
5
|
const extraGasLimit = 20000 // extra gas Limit to prevent tx failing if something change on pool state (till tx is in mempool)
|
|
@@ -10,14 +11,24 @@ export function createEthereumStakingService({
|
|
|
10
11
|
}) {
|
|
11
12
|
const staking = new EthereumStaking(asset)
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
function amountToCurrency({ asset, amount }) {
|
|
15
|
+
return isNumberUnit(amount) ? amount : asset.currency.parse(amount)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
async function delegate({ walletAccount, amount } = {}) {
|
|
19
|
+
const delegatorAddress = (
|
|
20
|
+
await assetClientInterface.getReceiveAddress({
|
|
21
|
+
assetName: asset.name,
|
|
22
|
+
walletAccount,
|
|
23
|
+
})
|
|
24
|
+
).toLowerCase()
|
|
25
|
+
amount = amountToCurrency({ asset, amount })
|
|
15
26
|
|
|
16
27
|
const { to, data } = await staking.stake({
|
|
17
28
|
amount,
|
|
18
29
|
})
|
|
19
30
|
|
|
20
|
-
const { gasPrice, gasLimit, fee } = await estimateTxFee(
|
|
31
|
+
const { gasPrice, gasLimit, fee } = await estimateTxFee(delegatorAddress, to, amount, data)
|
|
21
32
|
|
|
22
33
|
console.log(
|
|
23
34
|
`delegator address ${delegatorAddress} staking ${amount.toDefaultString({
|
|
@@ -38,12 +49,20 @@ export function createEthereumStakingService({
|
|
|
38
49
|
return txId
|
|
39
50
|
}
|
|
40
51
|
|
|
41
|
-
async function undelegate({ walletAccount,
|
|
52
|
+
async function undelegate({ walletAccount, amount } = {}) {
|
|
42
53
|
/*
|
|
43
54
|
unstakePending balance (not yet in validator) + unstake balance (in validator)
|
|
44
55
|
1. give priority to unstakePending (based on the amount)
|
|
45
56
|
2. unstake amount in validator.
|
|
46
57
|
*/
|
|
58
|
+
amount = amountToCurrency({ asset, amount })
|
|
59
|
+
|
|
60
|
+
const delegatorAddress = (
|
|
61
|
+
await assetClientInterface.getReceiveAddress({
|
|
62
|
+
assetName: asset.name,
|
|
63
|
+
walletAccount,
|
|
64
|
+
})
|
|
65
|
+
).toLowerCase()
|
|
47
66
|
|
|
48
67
|
const pendingAmount = await staking.pendingBalanceOf(delegatorAddress)
|
|
49
68
|
|
|
@@ -109,8 +128,14 @@ export function createEthereumStakingService({
|
|
|
109
128
|
return txId
|
|
110
129
|
}
|
|
111
130
|
|
|
112
|
-
async function claimUndelegatedBalance({ walletAccount
|
|
131
|
+
async function claimUndelegatedBalance({ walletAccount } = {}) {
|
|
113
132
|
// withdraw withdrawable balance (of a previous unstake)
|
|
133
|
+
const delegatorAddress = (
|
|
134
|
+
await assetClientInterface.getReceiveAddress({
|
|
135
|
+
assetName: asset.name,
|
|
136
|
+
walletAccount,
|
|
137
|
+
})
|
|
138
|
+
).toLowerCase()
|
|
114
139
|
|
|
115
140
|
const withdrawRequest = await staking.claimWithdrawRequest({
|
|
116
141
|
address: delegatorAddress,
|
|
@@ -131,7 +156,14 @@ export function createEthereumStakingService({
|
|
|
131
156
|
} else return null // -> no withdrawable balance
|
|
132
157
|
}
|
|
133
158
|
|
|
134
|
-
async function estimateDelegateOperation({
|
|
159
|
+
async function estimateDelegateOperation({ walletAccount, operation, args }) {
|
|
160
|
+
const delegatorAddress = (
|
|
161
|
+
await assetClientInterface.getReceiveAddress({
|
|
162
|
+
assetName: asset.name,
|
|
163
|
+
walletAccount,
|
|
164
|
+
})
|
|
165
|
+
).toLowerCase()
|
|
166
|
+
|
|
135
167
|
const NAMING_MAP = {
|
|
136
168
|
delegate: 'stake',
|
|
137
169
|
undelegate: 'unstake',
|
package/src/staking/index.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
1
|
import BN from 'bn.js'
|
|
2
2
|
import { createContract } from '@exodus/ethereum-lib'
|
|
3
3
|
import ethAssets from '@exodus/ethereum-meta'
|
|
4
|
-
import { getServerByName } from '
|
|
4
|
+
import { getServerByName } from '../../exodus-eth-server'
|
|
5
5
|
import { retry } from '@exodus/simple-retry'
|
|
6
6
|
import { bufferToHex } from '@exodus/ethereumjs-util'
|
|
7
7
|
|
|
8
|
-
const EVERSTAKE_VALIDATOR_CONTRACT_ADDR = '0xf30cf4ed712d3734161fdaab5b1dbb49fd2d0e5c'
|
|
9
|
-
const STAKING_MANAGER_ADDR = '0x5e3ef299fddf15eaa0432e6e66473ace8c13d908'
|
|
10
|
-
|
|
11
8
|
const polygonEthToken = ethAssets.find(({ name: tokenName }) => tokenName === 'polygon')
|
|
12
9
|
|
|
13
10
|
const RETRY_DELAYS = ['10s']
|
|
11
|
+
export class MaticStakingApi {
|
|
12
|
+
static EVERSTAKE_VALIDATOR_CONTRACT_ADDR = '0xf30cf4ed712d3734161fdaab5b1dbb49fd2d0e5c'
|
|
13
|
+
static STAKING_MANAGER_ADDR = '0x5e3ef299fddf15eaa0432e6e66473ace8c13d908'
|
|
14
14
|
|
|
15
|
-
export class MaticStaking {
|
|
16
15
|
constructor(
|
|
17
|
-
validatorId = EVERSTAKE_VALIDATOR_CONTRACT_ADDR,
|
|
18
|
-
stakeManagerAddr = STAKING_MANAGER_ADDR
|
|
16
|
+
validatorId = MaticStakingApi.EVERSTAKE_VALIDATOR_CONTRACT_ADDR,
|
|
17
|
+
stakeManagerAddr = MaticStakingApi.STAKING_MANAGER_ADDR
|
|
19
18
|
) {
|
|
20
19
|
// harcoded exchange rate from the validtor share contract
|
|
21
20
|
// in order to calculate claim unstake amount off-chain
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { MaticStakingApi } from './api'
|
|
2
|
+
export { createPolygonStakingService, getPolygonStakingInfo } from './service'
|
|
3
|
+
export {
|
|
4
|
+
isPolygonTx,
|
|
5
|
+
isPolygonDelegate,
|
|
6
|
+
isPolygonUndelegate,
|
|
7
|
+
isPolygonReward,
|
|
8
|
+
isPolygonClaimUndelegate,
|
|
9
|
+
} from './matic-staking-utils'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MaticStakingApi } from './api'
|
|
2
2
|
|
|
3
3
|
// function selector for buyVoucher(uint256 _amount, uint256 _minSharesToMint)
|
|
4
4
|
const DELEGATE = '0x6ab15071'
|
|
@@ -9,23 +9,16 @@ const CLAIM_REWARD = '0xc7b8981c'
|
|
|
9
9
|
// function selector for unstakeClaimTokens_new(uint256 unbondNonce)
|
|
10
10
|
const CLAIM_UNDELEGATE_BALANCE = '0xe97fddc2'
|
|
11
11
|
|
|
12
|
-
const STAKING_MANAGER_CONTRACT =
|
|
12
|
+
const STAKING_MANAGER_CONTRACT = MaticStakingApi.STAKING_MANAGER_ADDR
|
|
13
13
|
|
|
14
14
|
export const isPolygonTx = ({ coinName }) => coinName === 'polygon'
|
|
15
15
|
export const isPolygonDelegate = (tx) =>
|
|
16
16
|
isPolygonTx(tx) && tx.to === STAKING_MANAGER_CONTRACT && tx.data?.methodId === DELEGATE
|
|
17
17
|
export const isPolygonUndelegate = (tx) =>
|
|
18
|
-
isPolygonTx(tx) &&
|
|
19
|
-
tx.from[0] === STAKING_MANAGER_CONTRACT &&
|
|
20
|
-
tx.data &&
|
|
21
|
-
tx.data.methodId === UNDELEGATE
|
|
18
|
+
isPolygonTx(tx) && tx.from[0] === STAKING_MANAGER_CONTRACT && tx.data?.methodId === UNDELEGATE
|
|
22
19
|
export const isPolygonReward = (tx) =>
|
|
23
|
-
isPolygonTx(tx) &&
|
|
24
|
-
tx.from[0] === STAKING_MANAGER_CONTRACT &&
|
|
25
|
-
tx.data &&
|
|
26
|
-
tx.data.methodId === CLAIM_REWARD
|
|
20
|
+
isPolygonTx(tx) && tx.from[0] === STAKING_MANAGER_CONTRACT && tx.data?.methodId === CLAIM_REWARD
|
|
27
21
|
export const isPolygonClaimUndelegate = (tx) =>
|
|
28
22
|
isPolygonTx(tx) &&
|
|
29
23
|
tx.from[0] === STAKING_MANAGER_CONTRACT &&
|
|
30
|
-
tx.data
|
|
31
|
-
tx.data.methodId === CLAIM_UNDELEGATE_BALANCE
|
|
24
|
+
tx.data?.methodId === CLAIM_UNDELEGATE_BALANCE
|
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
import BN from 'bn.js'
|
|
2
|
+
import { getServer } from '../../exodus-eth-server'
|
|
3
|
+
import { estimateGasLimit } from '../../gas-estimation'
|
|
4
|
+
import { MaticStakingApi } from './api'
|
|
5
|
+
import { stakingProviderClientFactory } from '../staking-provider-client'
|
|
6
|
+
import { isNumberUnit } from '@exodus/currency'
|
|
7
|
+
|
|
8
|
+
export function createPolygonStakingService({ assetClientInterface, createAndBroadcastTX }) {
|
|
9
|
+
const stakingApi = new MaticStakingApi()
|
|
10
|
+
const assetName = 'ethereum'
|
|
11
|
+
const stakingProvider = stakingProviderClientFactory()
|
|
12
|
+
|
|
13
|
+
async function getStakeAssets() {
|
|
14
|
+
const { polygon: asset, ethereum: feeAsset } = await assetClientInterface.getAssetsForNetwork({
|
|
15
|
+
baseAssetName: assetName,
|
|
16
|
+
})
|
|
17
|
+
return { asset, feeAsset }
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function amountToCurrency({ asset, amount }) {
|
|
21
|
+
return isNumberUnit(amount) ? amount : asset.currency.parse(amount)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function delegate({ walletAccount, amount } = {}) {
|
|
25
|
+
const delegatorAddress = (
|
|
26
|
+
await assetClientInterface.getReceiveAddress({
|
|
27
|
+
assetName,
|
|
28
|
+
walletAccount,
|
|
29
|
+
})
|
|
30
|
+
).toLowerCase()
|
|
31
|
+
|
|
32
|
+
const { asset } = await getStakeAssets()
|
|
33
|
+
amount = amountToCurrency({ asset, amount })
|
|
34
|
+
|
|
35
|
+
const txApproveData = await stakingApi.approveStakeManager(amount)
|
|
36
|
+
let { gasPrice, gasLimit, fee } = await estimateTxFee(
|
|
37
|
+
delegatorAddress,
|
|
38
|
+
stakingApi.polygonContract.address,
|
|
39
|
+
txApproveData
|
|
40
|
+
)
|
|
41
|
+
await prepareAndSendTx({
|
|
42
|
+
walletAccount,
|
|
43
|
+
waitForConfirmation: true,
|
|
44
|
+
to: stakingApi.polygonContract.address,
|
|
45
|
+
txData: txApproveData,
|
|
46
|
+
gasPrice,
|
|
47
|
+
gasLimit,
|
|
48
|
+
fee,
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const txDelegateData = await stakingApi.delegate({ amount })
|
|
52
|
+
;({ gasPrice, gasLimit, fee } = await estimateTxFee(
|
|
53
|
+
delegatorAddress,
|
|
54
|
+
stakingApi.validatorShareContract.address,
|
|
55
|
+
txDelegateData
|
|
56
|
+
))
|
|
57
|
+
|
|
58
|
+
const txId = await prepareAndSendTx({
|
|
59
|
+
walletAccount,
|
|
60
|
+
to: stakingApi.validatorShareContract.address,
|
|
61
|
+
txData: txDelegateData,
|
|
62
|
+
gasPrice,
|
|
63
|
+
gasLimit,
|
|
64
|
+
fee,
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
await stakingProvider.notifyStaking({
|
|
68
|
+
txId,
|
|
69
|
+
asset: asset.name,
|
|
70
|
+
delegator: delegatorAddress,
|
|
71
|
+
amount: amount.toBaseString(),
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
return txId
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function undelegate({ walletAccount, amount } = {}) {
|
|
78
|
+
const delegatorAddress = (
|
|
79
|
+
await assetClientInterface.getReceiveAddress({
|
|
80
|
+
assetName,
|
|
81
|
+
walletAccount,
|
|
82
|
+
})
|
|
83
|
+
).toLowerCase()
|
|
84
|
+
|
|
85
|
+
const { asset } = await getStakeAssets()
|
|
86
|
+
amount = amountToCurrency({ asset, amount })
|
|
87
|
+
|
|
88
|
+
const txUndelegateData = await stakingApi.undelegate({ amount })
|
|
89
|
+
let { gasPrice, gasLimit, fee } = await estimateTxFee(
|
|
90
|
+
delegatorAddress.toLowerCase(),
|
|
91
|
+
stakingApi.validatorShareContract.address,
|
|
92
|
+
txUndelegateData
|
|
93
|
+
)
|
|
94
|
+
const txId = await prepareAndSendTx({
|
|
95
|
+
walletAccount,
|
|
96
|
+
to: stakingApi.validatorShareContract.address,
|
|
97
|
+
txData: txUndelegateData,
|
|
98
|
+
gasPrice,
|
|
99
|
+
gasLimit,
|
|
100
|
+
fee,
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
return txId
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function claimRewards({ walletAccount } = {}) {
|
|
107
|
+
const delegatorAddress = (
|
|
108
|
+
await assetClientInterface.getReceiveAddress({
|
|
109
|
+
assetName,
|
|
110
|
+
walletAccount,
|
|
111
|
+
})
|
|
112
|
+
).toLowerCase()
|
|
113
|
+
|
|
114
|
+
const txWithdrawRewardsData = await stakingApi.withdrawRewards()
|
|
115
|
+
let { gasPrice, gasLimit, fee } = await estimateTxFee(
|
|
116
|
+
delegatorAddress,
|
|
117
|
+
stakingApi.validatorShareContract.address,
|
|
118
|
+
txWithdrawRewardsData
|
|
119
|
+
)
|
|
120
|
+
const txId = await prepareAndSendTx({
|
|
121
|
+
walletAccount,
|
|
122
|
+
to: stakingApi.validatorShareContract.address,
|
|
123
|
+
txData: txWithdrawRewardsData,
|
|
124
|
+
gasPrice,
|
|
125
|
+
gasLimit,
|
|
126
|
+
fee,
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
return txId
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
async function claimUndelegatedBalance({ walletAccount, unbondNonce } = {}) {
|
|
133
|
+
const delegatorAddress = (
|
|
134
|
+
await assetClientInterface.getReceiveAddress({
|
|
135
|
+
assetName,
|
|
136
|
+
walletAccount,
|
|
137
|
+
})
|
|
138
|
+
).toLowerCase()
|
|
139
|
+
|
|
140
|
+
const { asset } = await getStakeAssets()
|
|
141
|
+
const { currency } = asset
|
|
142
|
+
const unstakedClaimInfo = await fetchUnstakedClaimInfo({
|
|
143
|
+
stakingApi,
|
|
144
|
+
delegator: delegatorAddress,
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const { unclaimedUndelegatedBalance } = await getUnstakedUnclaimedInfo({
|
|
148
|
+
stakingApi,
|
|
149
|
+
currency,
|
|
150
|
+
delegator: delegatorAddress,
|
|
151
|
+
...unstakedClaimInfo,
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const txClaimUndelegatedData = await stakingApi.claimUndelegatedBalance({ unbondNonce })
|
|
155
|
+
let { gasPrice, gasLimit, fee } = await estimateTxFee(
|
|
156
|
+
delegatorAddress,
|
|
157
|
+
stakingApi.validatorShareContract.address,
|
|
158
|
+
txClaimUndelegatedData
|
|
159
|
+
)
|
|
160
|
+
const txId = await prepareAndSendTx({
|
|
161
|
+
walletAccount,
|
|
162
|
+
to: stakingApi.validatorShareContract.address,
|
|
163
|
+
txData: txClaimUndelegatedData,
|
|
164
|
+
gasPrice,
|
|
165
|
+
gasLimit,
|
|
166
|
+
fee,
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
await stakingProvider.notifyUnstaking({
|
|
170
|
+
txId,
|
|
171
|
+
asset: asset.name,
|
|
172
|
+
delegator: delegatorAddress,
|
|
173
|
+
amount: unclaimedUndelegatedBalance.toBaseString(),
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
return txId
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function estimateDelegateOperation({ walletAccount, operation, args }) {
|
|
180
|
+
const delegateOperation = stakingApi[operation]
|
|
181
|
+
|
|
182
|
+
if (!delegateOperation) {
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
const delegatorAddress = (
|
|
186
|
+
await assetClientInterface.getReceiveAddress({
|
|
187
|
+
assetName,
|
|
188
|
+
walletAccount,
|
|
189
|
+
})
|
|
190
|
+
).toLowerCase()
|
|
191
|
+
|
|
192
|
+
const { amount } = args
|
|
193
|
+
if (amount) {
|
|
194
|
+
const { asset } = await getStakeAssets()
|
|
195
|
+
args = { ...args, amount: amountToCurrency({ asset, amount }) }
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const operationTxData = await delegateOperation({ ...args, walletAccount })
|
|
199
|
+
const { fee } = await estimateTxFee(
|
|
200
|
+
delegatorAddress,
|
|
201
|
+
stakingApi.validatorShareContract.address,
|
|
202
|
+
operationTxData
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
return fee
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Estimating delegete tx using {estimateGasLimit} function does not work
|
|
210
|
+
* as the execution reverts (due to missing MATIC approval).
|
|
211
|
+
* Instead, a fixed gas limit is use, which is:
|
|
212
|
+
*
|
|
213
|
+
* delegateGasLimit = ERC20ApproveGas + delegateGas
|
|
214
|
+
*
|
|
215
|
+
* This is just for displaying purposes and it's just an aproximation of the delegate gas cost,
|
|
216
|
+
* NOT the real fee cost
|
|
217
|
+
*/
|
|
218
|
+
async function estimateDelegateTxFee() {
|
|
219
|
+
// approx gas limits
|
|
220
|
+
const { ethereum } = await assetClientInterface.getAssetsForNetwork({
|
|
221
|
+
baseAssetName: 'ethereum',
|
|
222
|
+
})
|
|
223
|
+
const erc20ApproveGas = 4900
|
|
224
|
+
const delegateGas = 240000
|
|
225
|
+
const gasPrice = parseInt(await getServer(ethereum).gasPrice(), 16)
|
|
226
|
+
const extraPercentage = 20
|
|
227
|
+
|
|
228
|
+
const gasLimit = erc20ApproveGas + delegateGas
|
|
229
|
+
const gasLimitWithBuffer = new BN(gasLimit)
|
|
230
|
+
.imuln(100 + extraPercentage)
|
|
231
|
+
.idivn(100)
|
|
232
|
+
.toString()
|
|
233
|
+
|
|
234
|
+
const fee = new BN(gasLimitWithBuffer).mul(new BN(gasPrice))
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
gasLimit: gasLimitWithBuffer,
|
|
238
|
+
gasPrice,
|
|
239
|
+
fee: ethereum.currency.baseUnit(fee.toString()),
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function estimateTxFee(from, to, txInput, gasPrice = '0x0') {
|
|
244
|
+
const { ethereum } = await assetClientInterface.getAssetsForNetwork({
|
|
245
|
+
baseAssetName: 'ethereum',
|
|
246
|
+
})
|
|
247
|
+
const amount = ethereum.currency.ZERO
|
|
248
|
+
const gasLimit = await estimateGasLimit(
|
|
249
|
+
ethereum,
|
|
250
|
+
from,
|
|
251
|
+
to,
|
|
252
|
+
amount, // staking contracts does not require ETH amount to interact with
|
|
253
|
+
txInput,
|
|
254
|
+
gasPrice
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if (gasPrice === '0x0') {
|
|
258
|
+
gasPrice = await getServer(ethereum).gasPrice()
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
gasPrice = parseInt(gasPrice, 16)
|
|
262
|
+
const fee = new BN(gasPrice).mul(new BN(gasLimit))
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
gasLimit,
|
|
266
|
+
gasPrice: ethereum.currency.baseUnit(gasPrice),
|
|
267
|
+
fee: ethereum.currency.baseUnit(fee.toString()),
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function prepareAndSendTx({
|
|
272
|
+
walletAccount,
|
|
273
|
+
to,
|
|
274
|
+
txData: txInput,
|
|
275
|
+
gasPrice,
|
|
276
|
+
gasLimit,
|
|
277
|
+
fee,
|
|
278
|
+
waitForConfirmation = false,
|
|
279
|
+
} = {}) {
|
|
280
|
+
const { ethereum } = await assetClientInterface.getAssetsForNetwork({
|
|
281
|
+
baseAssetName: 'ethereum',
|
|
282
|
+
})
|
|
283
|
+
const sendTxArgs = {
|
|
284
|
+
asset: ethereum,
|
|
285
|
+
walletAccount,
|
|
286
|
+
address: to,
|
|
287
|
+
amount: ethereum.currency.ZERO,
|
|
288
|
+
options: {
|
|
289
|
+
shouldLog: true,
|
|
290
|
+
txInput,
|
|
291
|
+
gasPrice,
|
|
292
|
+
gasLimit,
|
|
293
|
+
feeAmount: fee,
|
|
294
|
+
},
|
|
295
|
+
waitForConfirmation,
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const { txId } = await createAndBroadcastTX(sendTxArgs)
|
|
299
|
+
|
|
300
|
+
return txId
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
delegate,
|
|
305
|
+
undelegate,
|
|
306
|
+
claimRewards,
|
|
307
|
+
claimUndelegatedBalance,
|
|
308
|
+
getPolygonStakingInfo,
|
|
309
|
+
estimateDelegateTxFee,
|
|
310
|
+
estimateDelegateOperation,
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function fetchUnstakedClaimInfo({ stakingApi, delegator }) {
|
|
315
|
+
const [unbondNonce, withdrawalDelay, currentEpoch, withdrawExchangeRate] = await Promise.all([
|
|
316
|
+
stakingApi.getCurrentUnbondNonce(delegator),
|
|
317
|
+
stakingApi.getWithdrawalDelay(),
|
|
318
|
+
stakingApi.getCurrentCheckpoint(),
|
|
319
|
+
stakingApi.getWithdrawExchangeRate(),
|
|
320
|
+
])
|
|
321
|
+
|
|
322
|
+
return {
|
|
323
|
+
unbondNonce,
|
|
324
|
+
withdrawalDelay,
|
|
325
|
+
currentEpoch,
|
|
326
|
+
withdrawExchangeRate,
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function calculateUnclaimedTokens({
|
|
331
|
+
currency,
|
|
332
|
+
exchangeRatePrecision,
|
|
333
|
+
withdrawExchangeRate,
|
|
334
|
+
shares,
|
|
335
|
+
canClaimUndelegatedBalance,
|
|
336
|
+
isUndelegateInProgress,
|
|
337
|
+
}) {
|
|
338
|
+
// see contract implementation
|
|
339
|
+
// https://github.com/maticnetwork/contracts/blob/1eb6960e511a967c15d4936904570a890d134fa6/contracts/staking/validatorShare/ValidatorShare.sol#L304
|
|
340
|
+
if (canClaimUndelegatedBalance || isUndelegateInProgress) {
|
|
341
|
+
const unclaimedTokens = withdrawExchangeRate
|
|
342
|
+
.mul(shares) // shares === validator tokens
|
|
343
|
+
.div(exchangeRatePrecision)
|
|
344
|
+
.toString()
|
|
345
|
+
|
|
346
|
+
return currency.baseUnit(unclaimedTokens)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return currency.ZERO
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function canClaimUndelegatedBalance({ shares, withdrawEpoch, isUndelegateInProgress }) {
|
|
353
|
+
const undelegateNotStarted = shares.isZero() && withdrawEpoch.isZero()
|
|
354
|
+
return !(isUndelegateInProgress || undelegateNotStarted)
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async function getUnstakedUnclaimedInfo({
|
|
358
|
+
stakingApi,
|
|
359
|
+
currency,
|
|
360
|
+
delegator,
|
|
361
|
+
unbondNonce,
|
|
362
|
+
currentEpoch,
|
|
363
|
+
withdrawalDelay,
|
|
364
|
+
withdrawExchangeRate,
|
|
365
|
+
}) {
|
|
366
|
+
const { withdrawEpoch, shares } = await stakingApi.getUnboundInfo(delegator, unbondNonce)
|
|
367
|
+
const exchangeRatePrecision = stakingApi.EXCHANGE_RATE_PRECISION
|
|
368
|
+
const isUndelegateInProgress =
|
|
369
|
+
!withdrawEpoch.isZero() && withdrawEpoch.add(withdrawalDelay).gte(currentEpoch)
|
|
370
|
+
const isUndelegatedBalanceClaimable = canClaimUndelegatedBalance({
|
|
371
|
+
shares,
|
|
372
|
+
withdrawEpoch,
|
|
373
|
+
isUndelegateInProgress,
|
|
374
|
+
})
|
|
375
|
+
const unclaimedUndelegatedBalance = calculateUnclaimedTokens({
|
|
376
|
+
currency,
|
|
377
|
+
exchangeRatePrecision,
|
|
378
|
+
withdrawExchangeRate,
|
|
379
|
+
shares,
|
|
380
|
+
canClaimUndelegatedBalance: isUndelegatedBalanceClaimable,
|
|
381
|
+
isUndelegateInProgress,
|
|
382
|
+
})
|
|
383
|
+
return {
|
|
384
|
+
isUndelegateInProgress,
|
|
385
|
+
canClaimUndelegatedBalance: isUndelegatedBalanceClaimable,
|
|
386
|
+
unclaimedUndelegatedBalance,
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
async function fetchRewardsInfo({ stakingApi, delegator, currency }) {
|
|
391
|
+
const [minRewardsToWithdraw, rewardsBalance] = await Promise.all([
|
|
392
|
+
stakingApi.getMinRewardsToWithdraw(),
|
|
393
|
+
stakingApi.getLiquidRewards(delegator),
|
|
394
|
+
])
|
|
395
|
+
const withdrawable = rewardsBalance.sub(minRewardsToWithdraw).gte(currency.ZERO)
|
|
396
|
+
? rewardsBalance
|
|
397
|
+
: currency.ZERO
|
|
398
|
+
|
|
399
|
+
return {
|
|
400
|
+
rewardsBalance,
|
|
401
|
+
minRewardsToWithdraw,
|
|
402
|
+
withdrawable,
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export async function getPolygonStakingInfo({ address, asset }) {
|
|
407
|
+
const { currency } = asset
|
|
408
|
+
const stakingApi = new MaticStakingApi()
|
|
409
|
+
const delegator = address.toLowerCase()
|
|
410
|
+
const [
|
|
411
|
+
delegatedBalance,
|
|
412
|
+
{ rewardsBalance, minRewardsToWithdraw, withdrawable },
|
|
413
|
+
{ unbondNonce, withdrawalDelay, currentEpoch, withdrawExchangeRate },
|
|
414
|
+
] = await Promise.all([
|
|
415
|
+
stakingApi.getTotalStake(delegator),
|
|
416
|
+
fetchRewardsInfo({ stakingApi, delegator, currency }),
|
|
417
|
+
fetchUnstakedClaimInfo({ stakingApi, delegator }),
|
|
418
|
+
])
|
|
419
|
+
const minDelegateAmount = currency.defaultUnit(1)
|
|
420
|
+
const isDelegating = !delegatedBalance.isZero
|
|
421
|
+
|
|
422
|
+
const unclaimedUndelegatedInfo = await getUnstakedUnclaimedInfo({
|
|
423
|
+
stakingApi,
|
|
424
|
+
currency,
|
|
425
|
+
delegator,
|
|
426
|
+
unbondNonce,
|
|
427
|
+
currentEpoch,
|
|
428
|
+
withdrawalDelay,
|
|
429
|
+
withdrawExchangeRate,
|
|
430
|
+
})
|
|
431
|
+
|
|
432
|
+
return {
|
|
433
|
+
rewardsBalance,
|
|
434
|
+
withdrawable,
|
|
435
|
+
unbondNonce,
|
|
436
|
+
isDelegating,
|
|
437
|
+
delegatedBalance,
|
|
438
|
+
minRewardsToWithdraw,
|
|
439
|
+
minDelegateAmount,
|
|
440
|
+
...unclaimedUndelegatedInfo,
|
|
441
|
+
}
|
|
442
|
+
}
|
|
@@ -76,6 +76,10 @@ export default function getLogItemsFromServerTx({
|
|
|
76
76
|
internalTransfers,
|
|
77
77
|
erc20Transfers,
|
|
78
78
|
}),
|
|
79
|
+
currencies: {
|
|
80
|
+
[asset.name]: asset.currency,
|
|
81
|
+
[asset.feeAsset.name]: asset.feeAsset.currency,
|
|
82
|
+
},
|
|
79
83
|
},
|
|
80
84
|
])
|
|
81
85
|
}
|
|
@@ -118,6 +122,10 @@ export default function getLogItemsFromServerTx({
|
|
|
118
122
|
? { from: [], to: tokenTransferToAddress, feeAmount, feeCoinName: token.feeAsset.name }
|
|
119
123
|
: { from: tokenFromAddresses }),
|
|
120
124
|
selfSend,
|
|
125
|
+
currencies: {
|
|
126
|
+
[token.name]: token.currency,
|
|
127
|
+
[token.feeAsset.name]: token.feeAsset.currency,
|
|
128
|
+
},
|
|
121
129
|
},
|
|
122
130
|
])
|
|
123
131
|
})
|
|
@@ -75,6 +75,10 @@ export default function getLogItemsFromServerTx({
|
|
|
75
75
|
serverTx,
|
|
76
76
|
ourWalletAddress,
|
|
77
77
|
}),
|
|
78
|
+
currencies: {
|
|
79
|
+
[asset.name]: asset.currency,
|
|
80
|
+
[asset.feeAsset.name]: asset.feeAsset.currency,
|
|
81
|
+
},
|
|
78
82
|
},
|
|
79
83
|
])
|
|
80
84
|
}
|
|
@@ -120,6 +124,10 @@ export default function getLogItemsFromServerTx({
|
|
|
120
124
|
? { from: [], to: tokenTransferToAddress, feeAmount, feeCoinName: asset.feeAsset.name }
|
|
121
125
|
: { from: tokenFromAddresses }),
|
|
122
126
|
selfSend,
|
|
127
|
+
currencies: {
|
|
128
|
+
[token.name]: token.currency,
|
|
129
|
+
[token.feeAsset.name]: token.feeAsset.currency,
|
|
130
|
+
},
|
|
123
131
|
},
|
|
124
132
|
])
|
|
125
133
|
})
|
package/LICENSE.md
DELETED
|
File without changes
|
|
File without changes
|