@exodus/ethereum-api 6.2.15 → 6.2.17
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 +3 -3
- package/src/staking/matic-staking.js +50 -22
- package/src/staking/matic-staking.md +67 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/ethereum-api",
|
|
3
|
-
"version": "6.2.
|
|
3
|
+
"version": "6.2.17",
|
|
4
4
|
"description": "Ethereum Api",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@exodus/asset-lib": "^3.7.1",
|
|
18
18
|
"@exodus/crypto": "^1.0.0-rc.0",
|
|
19
|
-
"@exodus/ethereum-lib": "^3.3.
|
|
19
|
+
"@exodus/ethereum-lib": "^3.3.9",
|
|
20
20
|
"@exodus/ethereumjs-util": "^7.1.0-exodus.6",
|
|
21
21
|
"@exodus/fetch": "^1.2.1",
|
|
22
22
|
"@exodus/simple-retry": "^0.0.6",
|
|
@@ -36,5 +36,5 @@
|
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@exodus/models": "^8.10.4"
|
|
38
38
|
},
|
|
39
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "52f5092deeb747acd04326890734c4c81e10c306"
|
|
40
40
|
}
|
|
@@ -18,6 +18,9 @@ export class MaticStaking {
|
|
|
18
18
|
validatorId = EVERSTAKE_VALIDATOR_CONTRACT_ADDR,
|
|
19
19
|
stakeManagerAddr = STAKING_MANAGER_ADDR
|
|
20
20
|
) {
|
|
21
|
+
// harcoded exchange rate from the validtor share contract
|
|
22
|
+
// in order to calculate claim unstake amount off-chain
|
|
23
|
+
this.EXCHANGE_RATE_PRECISION = new BN(10).pow(new BN(29))
|
|
21
24
|
this.validatorShareContract = createContract(validatorId, 'maticValidatorShare')
|
|
22
25
|
this.stakingManagerContract = createContract(stakeManagerAddr, 'maticStakingManager')
|
|
23
26
|
this.polygonContract = createContract(polygonEthToken.contract.current, 'polygon')
|
|
@@ -40,22 +43,20 @@ export class MaticStaking {
|
|
|
40
43
|
return retry(eth.ethCall, { delayTimesMs: RETRY_DELAYS })(data)
|
|
41
44
|
}
|
|
42
45
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
'increaseAllowance',
|
|
56
|
-
this.polygonContract.address,
|
|
57
|
-
amount.toBaseString()
|
|
46
|
+
getWithdrawalDelay = async () => {
|
|
47
|
+
const withdrawalDelay = await this.callReadFunctionContract(
|
|
48
|
+
this.stakingManagerContract,
|
|
49
|
+
'withdrawalDelay'
|
|
50
|
+
)
|
|
51
|
+
return toBN(withdrawalDelay)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getMinRewardsToWithdraw = async () => {
|
|
55
|
+
const minRewardsToWithdraw = await this.callReadFunctionContract(
|
|
56
|
+
this.validatorShareContract,
|
|
57
|
+
'minAmount'
|
|
58
58
|
)
|
|
59
|
+
return polygon.currency.baseUnit(minRewardsToWithdraw)
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
/**
|
|
@@ -85,10 +86,10 @@ export class MaticStaking {
|
|
|
85
86
|
_nonce
|
|
86
87
|
)
|
|
87
88
|
|
|
88
|
-
const [
|
|
89
|
+
const [shares, withdrawEpoch] = splitIn32BytesArray(unboundInfo)
|
|
89
90
|
return {
|
|
90
|
-
withdrawEpoch: toBN(withdrawEpoch
|
|
91
|
-
shares: toBN(shares
|
|
91
|
+
withdrawEpoch: toBN(withdrawEpoch),
|
|
92
|
+
shares: toBN(shares),
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
|
|
@@ -125,6 +126,33 @@ export class MaticStaking {
|
|
|
125
126
|
return polygon.currency.baseUnit(toBN(amount).toString())
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
getWithdrawExchangeRate = async () => {
|
|
130
|
+
const withdrawExchangeRate = await this.callReadFunctionContract(
|
|
131
|
+
this.validatorShareContract,
|
|
132
|
+
'withdrawExchangeRate'
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
return toBN(withdrawExchangeRate)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Approves StakeManager contract for withdrawing {amount} in Matic tokens
|
|
140
|
+
* when users stakes.
|
|
141
|
+
* This function needs to be called before calling delegate function so that staking
|
|
142
|
+
* can properly work
|
|
143
|
+
* This also partially address the front running attack on ERC20 approve function:
|
|
144
|
+
* https://github.com/OpenZeppelin/openzeppelin-contracts/blob/f959d7e4e6ee0b022b41e5b644c79369869d8411/contracts/token/ERC20/ERC20.sol#L165-L206
|
|
145
|
+
* @param amount Matic tokens to be approved for StakeManager to withdraw (polygon currency)
|
|
146
|
+
*/
|
|
147
|
+
approveStakeManager = (amount) => {
|
|
148
|
+
return this.buildTxData(
|
|
149
|
+
this.polygonContract,
|
|
150
|
+
'increaseAllowance',
|
|
151
|
+
this.stakingManagerContract.address,
|
|
152
|
+
amount.toBaseString()
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
128
156
|
restakeReward = () => {
|
|
129
157
|
return this.buildTxData(this.validatorShareContract, 'restake')
|
|
130
158
|
}
|
|
@@ -134,7 +162,7 @@ export class MaticStaking {
|
|
|
134
162
|
}
|
|
135
163
|
|
|
136
164
|
delegate = ({ amount }) => {
|
|
137
|
-
return this.buildTxData(this.validatorShareContract, 'buyVoucher', amount.toBaseString(), 0)
|
|
165
|
+
return this.buildTxData(this.validatorShareContract, 'buyVoucher', amount.toBaseString(), '0')
|
|
138
166
|
}
|
|
139
167
|
|
|
140
168
|
undelegate = ({ amount, maximumSharesToBurn }) => {
|
|
@@ -148,10 +176,10 @@ export class MaticStaking {
|
|
|
148
176
|
}
|
|
149
177
|
|
|
150
178
|
/**
|
|
151
|
-
* @param {number}
|
|
179
|
+
* @param {number} unbondNonce the unbond nonce from where delegator claim its staked tokens
|
|
152
180
|
*/
|
|
153
|
-
|
|
154
|
-
return this.buildTxData(this.validatorShareContract, 'unstakeClaimTokens_new',
|
|
181
|
+
claimUndelegatedBalance = ({ unbondNonce }) => {
|
|
182
|
+
return this.buildTxData(this.validatorShareContract, 'unstakeClaimTokens_new', unbondNonce)
|
|
155
183
|
}
|
|
156
184
|
}
|
|
157
185
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
## Staking in Polygon (MATIC)
|
|
3
|
+
|
|
4
|
+
## How it works?
|
|
5
|
+
Polygon Staking happens in the **Ethereum network**, it is implemented by a set of smart contracts deployed on the ETH mainnet.
|
|
6
|
+
Staking is made by delegating the `ERC20 MATIC` token to those smart contracts which handle the `(un)staking/rewards` process and it shouldn't be confused with the Native asset in **Polygon network** (`MATIC NATIVE`).
|
|
7
|
+
|
|
8
|
+
Stakers are divided into `validators`, `delegators`, and watchers (for fraud reporting).
|
|
9
|
+
|
|
10
|
+
## Contracts
|
|
11
|
+
|
|
12
|
+
### StakeManager contract
|
|
13
|
+
`StakeManager` is the main contract for handling validator related activities like checkPoint signature verification, reward distribution, and stake management. Since the contract is using NFT ID as a source of ownership, change of ownership and signer won't affect anything in the system. [see](https://wiki.polygon.technology/docs/pos/contracts/stakingmanager).
|
|
14
|
+
|
|
15
|
+
### ValidatorShare contract
|
|
16
|
+
For delegation staking each validator has its own deployed **contract**, this contract has the logic to `stake/unstake` as delegators, but it also acts as an `ERC20`, this `ERC20` token is what we know as the shares token. Shares token are calculated based on the total amount staked in the contract, varying from time to time
|
|
17
|
+
When delegators stake matic, they call `buyVoucher()` , the contract receives the MATIC tokens to stake (approval is needed on Matic token contract) and it calculates and mints the number of token shares that correspond to that staked amount. [see](https://wiki.polygon.technology/docs/pos/contracts/delegation).
|
|
18
|
+
|
|
19
|
+
### Staking Anatomy
|
|
20
|
+
Example taken from the docs:
|
|
21
|
+
|
|
22
|
+
*Polygon supports delegation via validator shares. By using this design, it is easier to distribute rewards and slash with scale (thousands of delegators) on Ethereum contracts without much computation.
|
|
23
|
+
Delegators delegate by purchasing shares of a finite pool from validators. Each validator will have their own validator share token. Let's call these fungible tokens `VATIC` for a validator `A`. As soon as a user delegates to a validator `A`, they will be issued `VATIC` based on an exchange rate of `MATIC/VATIC` pair. As users accrue value the exchange rate indicates that they can now withdraw more `MATIC` for each `VATIC` and when users get slashed, users withdraw less `MATIC` for their `VATIC`.
|
|
24
|
+
Note that `MATIC` is a staking token. A delegator needs to have `MATIC` tokens to participate in the delegation.
|
|
25
|
+
Initially, a delegator `D` buys tokens from validator `A` specific pool when `1 MATIC per 1 VATIC`.
|
|
26
|
+
When a validator gets rewarded with more `MATIC` tokens, new tokens are added to the pool. Let's say with the current pool of `100 MATIC` tokens, `10 MATIC` rewards are added to the pool. But since the total supply of `VATIC` tokens didn't change due to rewards, the exchange rate becomes `1 MATIC per 0.9 VATIC`. Now, delegator `D` gets more `MATIC` for the same shares.
|
|
27
|
+
`VATIC`: Validator specific minted validator share tokens (ERC20 tokens)*
|
|
28
|
+
|
|
29
|
+
#### Rewards
|
|
30
|
+
Delegators can do with their rewards the following:
|
|
31
|
+
withdraw via `withdrawRewards()` or
|
|
32
|
+
`restake` (earned rewards are put as stake in the contract via `restake()`)
|
|
33
|
+
|
|
34
|
+
#### Unstaking
|
|
35
|
+
For delegators to unstake, there are two steps:
|
|
36
|
+
1. `sellVoucher_new()`
|
|
37
|
+
2. `unstakeClaimTokens()`
|
|
38
|
+
|
|
39
|
+
**Note**: there are some methods in the validator share contract with the suffix `_new`, for instance:
|
|
40
|
+
`sellVoucher()` and `sellVoucher_new()` this is because the recent changes on the smart contract to support the new exit API (**unstake** and **claim** tokens)*
|
|
41
|
+
|
|
42
|
+
**sellVoucher** method calculates the token shares that correspond to the staked MATIC at that time, and burns those share tokens, it also transfers the rewards, and makes changes in the contract to update the total stake held in the contract .
|
|
43
|
+
Basically it prepares the contract to let delegators withdraw their staked MATIC in a second step once the **withdrawal delay** it's ben fulfilled.
|
|
44
|
+
|
|
45
|
+
#### Unstake Claim Tokens
|
|
46
|
+
**unstakeClaimTokens** makes the actual withdraw of the staked tokens, sellVoucher does not transfer the staked MATIC back to the delegator, we need to call this function after the withdrawal delay has been fulfilled, so that delegators can get their stake amount back to their wallet.
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
### Staking Bussines Rules
|
|
50
|
+
|
|
51
|
+
All of these rules can be queried from the smart contracts, except `minimum amount to stake`, this is defined by Exodus.
|
|
52
|
+
|
|
53
|
+
- **Minimum rewards to withdraw**: 1 MATIC (contract rule)
|
|
54
|
+
- **Minimum amount to stake**: 1 MATIC (exodus rule)
|
|
55
|
+
- **Withdrawal delay**: The amount of time delegators must wait before claiming their staked amount. Varies depending on the contract governance
|
|
56
|
+
- **Withdrawal exchange rate**: The exchange rate used to convert tokens to shares and vice-versa. Varies depending on the number of MATIC in the withdraw pool share (affected by the earned rewards).
|
|
57
|
+
- **Unstaking period**: immediately, but staked MATIC tokens are not available to be withdrawn before withdrawal delay
|
|
58
|
+
- **Claim unstaked tokens**: after 3-4 days has passed since unstaked was called, the unstaked tokens can be claimed by the delegator.
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
Useful resources:
|
|
62
|
+
|
|
63
|
+
[Polygon docs](https://wiki.polygon.technology/docs/pos/polygon-architecture/)
|
|
64
|
+
[Polygon Staking](https://wiki.polygon.technology/docs/pos/contracts/delegation)
|
|
65
|
+
[StakeManager contract](https://github.com/maticnetwork/contracts/blob/main/contracts/staking/stakeManager/StakeManager.sol)
|
|
66
|
+
[Validator share contract](https://github.com/maticnetwork/contracts/blob/main/contracts/staking/stakeManager/StakeManager.sol)
|
|
67
|
+
|