Skip to content

Commit 066f2eb

Browse files
authored
Merge pull request from GHSA-7477-q5g6-cj48
fix: ensure thawing period when unstaking always rounds up
2 parents 6aca892 + 8e164ba commit 066f2eb

File tree

3 files changed

+42
-4
lines changed

3 files changed

+42
-4
lines changed

packages/contracts/contracts/staking/libs/MathUtils.sol

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,23 @@ library MathUtils {
1414
/**
1515
* @dev Calculates the weighted average of two values pondering each of these
1616
* values based on configured weights. The contribution of each value N is
17-
* weightN/(weightA + weightB).
17+
* weightN/(weightA + weightB). The calculation rounds up to ensure the result
18+
* is always greater than the smallest of the two values.
1819
* @param valueA The amount for value A
1920
* @param weightA The weight to use for value A
2021
* @param valueB The amount for value B
2122
* @param weightB The weight to use for value B
2223
*/
23-
function weightedAverage(
24+
function weightedAverageRoundingUp(
2425
uint256 valueA,
2526
uint256 weightA,
2627
uint256 valueB,
2728
uint256 weightB
2829
) internal pure returns (uint256) {
29-
return valueA.mul(weightA).add(valueB.mul(weightB)).div(weightA.add(weightB));
30+
return
31+
valueA.mul(weightA).add(valueB.mul(weightB)).add(weightA.add(weightB).sub(1)).div(
32+
weightA.add(weightB)
33+
);
3034
}
3135

3236
/**

packages/contracts/contracts/staking/libs/Stakes.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ library Stakes {
7373
// Take into account period averaging for multiple unstake requests
7474
uint256 lockingPeriod = _period;
7575
if (stake.tokensLocked > 0) {
76-
lockingPeriod = MathUtils.weightedAverage(
76+
lockingPeriod = MathUtils.weightedAverageRoundingUp(
7777
MathUtils.diffOrZero(stake.tokensLockedUntil, block.number), // Remaining thawing period
7878
stake.tokensLocked, // Weighted by remaining unstaked tokens
7979
_period, // Thawing period

packages/contracts/test/unit/staking/staking.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,40 @@ describe('Staking:Stakes', () => {
239239
expect(afterIndexerStake.tokensLockedUntil).eq(expectedLockedUntil)
240240
})
241241

242+
it('should always increase the thawing period on subsequent unstakes', async function () {
243+
const tokensToUnstake = toGRT('10')
244+
const tokensToUnstakeSecondTime = toGRT('0.000001')
245+
const thawingPeriod = toBN(await staking.thawingPeriod())
246+
247+
// Unstake (1)
248+
const tx1 = await staking.connect(indexer).unstake(tokensToUnstake)
249+
const receipt1 = await tx1.wait()
250+
const event1: Event = receipt1.events.pop()
251+
const tokensLockedUntil1 = event1.args['until']
252+
253+
// Move forward before the tokens are unlocked for withdrawal
254+
await helpers.mineUpTo(tokensLockedUntil1.sub(5))
255+
256+
// Calculate locking time for tokens taking into account the previous unstake request
257+
const currentBlock = await helpers.latestBlock()
258+
259+
// Ensure at least 1 block is added (i.e. the weighted average rounds up)
260+
const expectedLockedUntil = tokensLockedUntil1.add(1)
261+
262+
// Unstake (2)
263+
const tx2 = await staking.connect(indexer).unstake(tokensToUnstakeSecondTime)
264+
const receipt2 = await tx2.wait()
265+
266+
// Verify events
267+
const event2: Event = receipt2.events.pop()
268+
expect(event2.args['until']).eq(expectedLockedUntil)
269+
270+
// Verify state
271+
const afterIndexerStake = await staking.stakes(indexer.address)
272+
expect(afterIndexerStake.tokensLocked).eq(tokensToUnstake.add(tokensToUnstakeSecondTime)) // we unstaked two times
273+
expect(afterIndexerStake.tokensLockedUntil).eq(expectedLockedUntil)
274+
})
275+
242276
it('should unstake and withdraw if some tokens are unthawed', async function () {
243277
const tokensToUnstake = toGRT('10')
244278
const thawingPeriod = toBN(await staking.thawingPeriod())

0 commit comments

Comments
 (0)