diff --git a/packages/horizon/test/staking/HorizonStaking.t.sol b/packages/horizon/test/staking/HorizonStaking.t.sol index 64dbc1630..bf0106fca 100644 --- a/packages/horizon/test/staking/HorizonStaking.t.sol +++ b/packages/horizon/test/staking/HorizonStaking.t.sol @@ -290,9 +290,9 @@ contract HorizonStakingTest is HorizonStakingSharedTest, IHorizonStakingTypes { vm.store(address(staking), bytes32(uint256(serviceProviderBaseSlot) + 4), bytes32(_tokensProvisioned)); } - function _slash(uint256 tokens, uint256 verifierCutAmount) internal { - uint256 beforeProviderTokens = staking.getProviderTokensAvailable(users.indexer, subgraphDataServiceAddress); - uint256 beforeDelegationTokens = staking.getDelegatedTokensAvailable(users.indexer, subgraphDataServiceAddress); + function _slash(address serviceProvider, address verifier, uint256 tokens, uint256 verifierCutAmount) internal { + uint256 beforeProviderTokens = staking.getProviderTokensAvailable(serviceProvider, verifier); + uint256 beforeDelegationTokens = staking.getDelegatedTokensAvailable(serviceProvider, verifier); bool isDelegationSlashingEnabled = staking.isDelegationSlashingEnabled(); // Calculate expected tokens after slashing @@ -306,40 +306,40 @@ contract HorizonStakingTest is HorizonStakingSharedTest, IHorizonStakingTypes { vm.expectEmit(address(staking)); if (verifierCutAmount > 0) { emit IHorizonStakingMain.VerifierTokensSent( - users.indexer, - subgraphDataServiceAddress, - subgraphDataServiceAddress, + serviceProvider, + verifier, + verifier, verifierCutAmount ); } - emit IHorizonStakingMain.ProvisionSlashed(users.indexer, subgraphDataServiceAddress, providerTokensSlashed); + emit IHorizonStakingMain.ProvisionSlashed(serviceProvider, verifier, providerTokensSlashed); if (isDelegationSlashingEnabled) { emit IHorizonStakingMain.DelegationSlashed( - users.indexer, - subgraphDataServiceAddress, + serviceProvider, + verifier, delegationTokensSlashed ); } else { emit IHorizonStakingMain.DelegationSlashingSkipped( - users.indexer, - subgraphDataServiceAddress, + serviceProvider, + verifier, delegationTokensSlashed ); } - staking.slash(users.indexer, tokens, verifierCutAmount, subgraphDataServiceAddress); + staking.slash(serviceProvider, tokens, verifierCutAmount, verifier); if (!isDelegationSlashingEnabled) { expectedDelegationTokensAfterSlashing = beforeDelegationTokens; } - uint256 provisionTokens = staking.getProviderTokensAvailable(users.indexer, subgraphDataServiceAddress); + uint256 provisionTokens = staking.getProviderTokensAvailable(serviceProvider, verifier); assertEq(provisionTokens, expectedProviderTokensAfterSlashing); - uint256 delegationTokens = staking.getDelegatedTokensAvailable(users.indexer, subgraphDataServiceAddress); + uint256 delegationTokens = staking.getDelegatedTokensAvailable(serviceProvider, verifier); assertEq(delegationTokens, expectedDelegationTokensAfterSlashing); - uint256 verifierTokens = token.balanceOf(subgraphDataServiceAddress); + uint256 verifierTokens = token.balanceOf(verifier); assertEq(verifierTokens, verifierCutAmount); } diff --git a/packages/horizon/test/staking/delegation/delegate.t.sol b/packages/horizon/test/staking/delegation/delegate.t.sol index 7136cf721..939893794 100644 --- a/packages/horizon/test/staking/delegation/delegate.t.sol +++ b/packages/horizon/test/staking/delegation/delegate.t.sol @@ -78,7 +78,7 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); resetPrank(subgraphDataServiceAddress); - _slash(tokens + delegationTokens, 0); + _slash(users.indexer, subgraphDataServiceAddress, tokens + delegationTokens, 0); resetPrank(users.delegator); token.approve(address(staking), delegationTokens); @@ -101,7 +101,7 @@ contract HorizonStakingDelegateTest is HorizonStakingTest { _undelegate(users.indexer, subgraphDataServiceAddress, delegation.shares); resetPrank(subgraphDataServiceAddress); - _slash(tokens + delegationTokens, 0); + _slash(users.indexer, subgraphDataServiceAddress, tokens + delegationTokens, 0); resetPrank(users.delegator); token.approve(address(staking), delegationTokens); diff --git a/packages/horizon/test/staking/delegation/undelegate.t.sol b/packages/horizon/test/staking/delegation/undelegate.t.sol index 8c578b768..54e16d084 100644 --- a/packages/horizon/test/staking/delegation/undelegate.t.sol +++ b/packages/horizon/test/staking/delegation/undelegate.t.sol @@ -122,7 +122,7 @@ contract HorizonStakingUndelegateTest is HorizonStakingTest { _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); resetPrank(subgraphDataServiceAddress); - _slash(tokens + delegationTokens, 0); + _slash(users.indexer, subgraphDataServiceAddress, tokens + delegationTokens, 0); resetPrank(users.delegator); Delegation memory delegation = _getDelegation(subgraphDataServiceAddress); diff --git a/packages/horizon/test/staking/delegation/withdraw.t.sol b/packages/horizon/test/staking/delegation/withdraw.t.sol index cffcad565..dc7785ed4 100644 --- a/packages/horizon/test/staking/delegation/withdraw.t.sol +++ b/packages/horizon/test/staking/delegation/withdraw.t.sol @@ -190,7 +190,7 @@ contract HorizonStakingWithdrawDelegationTest is HorizonStakingTest { skip(MAX_THAWING_PERIOD + 1); resetPrank(subgraphDataServiceAddress); - _slash(tokens + delegationTokens, 0); + _slash(users.indexer, subgraphDataServiceAddress, tokens + delegationTokens, 0); resetPrank(users.delegator); vm.expectRevert(abi.encodeWithSelector( diff --git a/packages/horizon/test/staking/slash/slash.t.sol b/packages/horizon/test/staking/slash/slash.t.sol index 8f7d086f5..1644e336e 100644 --- a/packages/horizon/test/staking/slash/slash.t.sol +++ b/packages/horizon/test/staking/slash/slash.t.sol @@ -33,7 +33,7 @@ contract HorizonStakingSlashTest is HorizonStakingTest { vm.assume(verifierCutAmount <= maxVerifierTokens); vm.startPrank(subgraphDataServiceAddress); - _slash(slashTokens, verifierCutAmount); + _slash(users.indexer, subgraphDataServiceAddress, slashTokens, verifierCutAmount); } function testSlash_DelegationDisabled_SlashingOverProviderTokens( @@ -52,7 +52,7 @@ contract HorizonStakingSlashTest is HorizonStakingTest { _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); vm.startPrank(subgraphDataServiceAddress); - _slash(slashTokens, verifierCutAmount); + _slash(users.indexer, subgraphDataServiceAddress, slashTokens, verifierCutAmount); } function testSlash_DelegationEnabled_SlashingOverProviderTokens( @@ -71,7 +71,7 @@ contract HorizonStakingSlashTest is HorizonStakingTest { _delegate(users.indexer, subgraphDataServiceAddress, delegationTokens, 0); vm.startPrank(subgraphDataServiceAddress); - _slash(slashTokens, verifierCutAmount); + _slash(users.indexer, subgraphDataServiceAddress, slashTokens, verifierCutAmount); } function testSlash_OverProvisionSize( @@ -83,7 +83,7 @@ contract HorizonStakingSlashTest is HorizonStakingTest { vm.assume(slashTokens > tokens + delegationTokens); vm.startPrank(subgraphDataServiceAddress); - _slash(slashTokens, 0); + _slash(users.indexer, subgraphDataServiceAddress, slashTokens, 0); } function testSlash_RevertWhen_NoProvision( diff --git a/packages/horizon/test/staking/transfer-tools/ttools.t.sol b/packages/horizon/test/staking/transfer-tools/ttools.t.sol index fcc3c7d69..71bf2bb3e 100644 --- a/packages/horizon/test/staking/transfer-tools/ttools.t.sol +++ b/packages/horizon/test/staking/transfer-tools/ttools.t.sol @@ -101,6 +101,35 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { _onTokenTransfer_ReceiveDelegation(counterpartStaking, amount, data); } + function testOnTransfer_ReceiveDelegation_WhenInvalidPool(uint256 amount) public useDelegationSlashing(true) { + amount = bound(amount, 1 ether, MAX_STAKING_TOKENS); + uint256 originalDelegationAmount = 10 ether; + uint256 provisionSize = 100 ether; + + // create provision and legacy delegation pool - this is done by the bridge when indexers move to L2 + resetPrank(users.indexer); + _createProvision(subgraphDataServiceLegacyAddress, provisionSize, 0, 1 days); + + // initialize the delegation pool + resetPrank(users.delegator); + _delegateLegacy(users.indexer, originalDelegationAmount); + + // slash the entire provision + resetPrank(subgraphDataServiceLegacyAddress); + _slash(users.indexer, subgraphDataServiceLegacyAddress, provisionSize + originalDelegationAmount, 0); + + // send amount to staking contract - this should be done by the bridge + resetPrank(users.delegator); + token.transfer(address(staking), amount); + + resetPrank(graphTokenGatewayAddress); + bytes memory data = abi.encode( + uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE), + abi.encode(users.indexer, users.delegator) + ); + _onTokenTransfer_ReceiveDelegation(counterpartStaking, amount, data); + } + /** * HELPERS */ @@ -128,8 +157,11 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { ? tokens : ((tokens * beforePool.shares) / (beforePool.tokens - beforePool.tokensThawing)); + bool earlyExit = (calcShares == 0 || tokens < 1 ether) || + (beforePool.tokens == 0 && (beforePool.shares != 0 || beforePool.sharesThawing != 0)); + // onTokenTransfer - if (calcShares == 0 || tokens < 1 ether) { + if (earlyExit) { vm.expectEmit(); emit Transfer(address(staking), delegator, tokens); vm.expectEmit(); @@ -158,7 +190,7 @@ contract HorizonStakingTransferToolsTest is HorizonStakingTest { uint256 deltaShares = afterDelegation.shares - beforeDelegation.shares; // assertions - if (calcShares == 0 || tokens < 1 ether) { + if (earlyExit) { assertEq(beforePool.tokens, afterPool.tokens); assertEq(beforePool.shares, afterPool.shares); assertEq(beforePool.tokensThawing, afterPool.tokensThawing);