Skip to content

Commit 167055a

Browse files
committed
fix: round down tokens thawing when slashing (TRST-H04)
1 parent d02f410 commit 167055a

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

packages/horizon/contracts/staking/HorizonStaking.sol

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,8 +461,8 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
461461
_graphToken().burnTokens(providerTokensSlashed - tokensVerifier);
462462

463463
// Provision accounting
464-
// TODO check for rounding issues
465-
uint256 provisionFractionSlashed = (providerTokensSlashed * FIXED_POINT_PRECISION) / prov.tokens;
464+
uint256 provisionFractionSlashed = (providerTokensSlashed * FIXED_POINT_PRECISION + prov.tokens - 1) /
465+
prov.tokens;
466466
prov.tokensThawing =
467467
(prov.tokensThawing * (FIXED_POINT_PRECISION - provisionFractionSlashed)) /
468468
(FIXED_POINT_PRECISION);
@@ -497,7 +497,8 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
497497
_graphToken().burnTokens(tokensToSlash);
498498

499499
// Delegation pool accounting
500-
uint256 delegationFractionSlashed = (tokensToSlash * FIXED_POINT_PRECISION) / pool.tokens;
500+
uint256 delegationFractionSlashed = (tokensToSlash * FIXED_POINT_PRECISION + pool.tokens - 1) /
501+
pool.tokens;
501502
pool.tokens = pool.tokens - tokensToSlash;
502503
pool.tokensThawing =
503504
(pool.tokensThawing * (FIXED_POINT_PRECISION - delegationFractionSlashed)) /

packages/horizon/test/shared/horizon-staking/HorizonStakingShared.t.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,7 +1488,7 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest {
14881488
uint256 tokensSlashed = calcValues.providerTokensSlashed +
14891489
(isDelegationSlashingEnabled ? calcValues.delegationTokensSlashed : 0);
14901490
uint256 provisionThawingTokens = (before.provision.tokensThawing *
1491-
(1e18 - ((calcValues.providerTokensSlashed * 1e18) / before.provision.tokens))) / (1e18);
1491+
(1e18 - ((calcValues.providerTokensSlashed * 1e18 + before.provision.tokens - 1) / before.provision.tokens))) / (1e18);
14921492

14931493
// assert
14941494
assertEq(afterProvision.tokens + calcValues.providerTokensSlashed, before.provision.tokens);
@@ -1506,7 +1506,7 @@ abstract contract HorizonStakingSharedTest is GraphBaseTest {
15061506
(before.provision.sharesThawing != 0 && afterProvision.sharesThawing == 0) ? before.provision.thawingNonce + 1 : before.provision.thawingNonce);
15071507
if (isDelegationSlashingEnabled) {
15081508
uint256 poolThawingTokens = (before.pool.tokensThawing *
1509-
(1e18 - ((calcValues.delegationTokensSlashed * 1e18) / before.pool.tokens))) / (1e18);
1509+
(1e18 - ((calcValues.delegationTokensSlashed * 1e18 + before.pool.tokens - 1) / before.pool.tokens))) / (1e18);
15101510
assertEq(afterPool.tokens + calcValues.delegationTokensSlashed, before.pool.tokens);
15111511
assertEq(afterPool.shares, before.pool.shares);
15121512
assertEq(afterPool.tokensThawing, poolThawingTokens);

packages/horizon/test/staking/slash/slash.t.sol

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,46 @@ contract HorizonStakingSlashTest is HorizonStakingTest {
140140
vm.startPrank(subgraphDataServiceAddress);
141141
_slash(users.indexer, subgraphDataServiceAddress, tokens + delegationTokens, 0);
142142
}
143+
144+
function testSlash_RoundDown_TokensThawing_Provision() public useIndexer {
145+
uint256 tokens = 1 ether + 1;
146+
_useProvision(subgraphDataServiceAddress, tokens, MAX_PPM, MAX_THAWING_PERIOD);
147+
148+
_thaw(users.indexer, subgraphDataServiceAddress, tokens);
149+
150+
resetPrank(subgraphDataServiceAddress);
151+
_slash(users.indexer, subgraphDataServiceAddress, 1, 0);
152+
153+
resetPrank(users.indexer);
154+
Provision memory provision = staking.getProvision(users.indexer, subgraphDataServiceAddress);
155+
assertEq(provision.tokens, tokens - 1);
156+
// Tokens thawing should be rounded down
157+
assertEq(provision.tokensThawing, tokens - 2);
158+
}
159+
160+
function testSlash_RoundDown_TokensThawing_Delegation(
161+
uint256 tokens
162+
) public useIndexer useProvision(tokens, MAX_PPM, 0) useDelegationSlashing {
163+
resetPrank(users.delegator);
164+
uint256 delegationTokens = 1 ether + 1;
165+
_delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0);
166+
167+
DelegationInternal memory delegation = _getStorage_Delegation(
168+
users.indexer,
169+
subgraphDataServiceAddress,
170+
users.delegator,
171+
false
172+
);
173+
_undelegate(users.indexer, subgraphDataServiceAddress, delegation.shares);
174+
175+
resetPrank(subgraphDataServiceAddress);
176+
// Slash 1 token from delegation
177+
_slash(users.indexer, subgraphDataServiceAddress, tokens + 1, 0);
178+
179+
resetPrank(users.delegator);
180+
DelegationPool memory pool = staking.getDelegationPool(users.indexer, subgraphDataServiceAddress);
181+
assertEq(pool.tokens, delegationTokens - 1);
182+
// Tokens thawing should be rounded down
183+
assertEq(pool.tokensThawing, delegationTokens - 2);
184+
}
143185
}

0 commit comments

Comments
 (0)