Skip to content

Commit b4d33e0

Browse files
committed
chore: use custom errors
Signed-off-by: Tomás Migone <[email protected]>
1 parent 5557c5e commit b4d33e0

File tree

6 files changed

+83
-15
lines changed

6 files changed

+83
-15
lines changed

packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol

+16
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,11 @@ interface IHorizonStakingMain {
471471
*/
472472
error HorizonStakingTooManyThawRequests();
473473

474+
/**
475+
* @notice Thrown when attempting to withdraw tokens that have not thawed (legacy undelegate).
476+
*/
477+
error HorizonStakingNothingToWithdraw();
478+
474479
// -- Errors: misc --
475480
/**
476481
* @notice Thrown during the transition period when attempting to withdraw tokens that are still thawing.
@@ -498,6 +503,11 @@ interface IHorizonStakingMain {
498503
*/
499504
error HorizonStakingInvalidDelegationFeeCut(uint256 feeCut);
500505

506+
/**
507+
* @notice Thrown when a legacy slash fails.
508+
*/
509+
error HorizonStakingLegacySlashFailed();
510+
501511
// -- Functions --
502512

503513
/**
@@ -972,4 +982,10 @@ interface IHorizonStakingMain {
972982
* @return Whether the operator is authorized or not
973983
*/
974984
function isAuthorized(address serviceProvider, address verifier, address operator) external view returns (bool);
985+
986+
/**
987+
* @notice Get the address of the staking extension.
988+
* @return The address of the staking extension
989+
*/
990+
function getStakingExtension() external view returns (address);
975991
}

packages/horizon/contracts/staking/HorizonStaking.sol

+12-6
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ import { PPMMath } from "../libraries/PPMMath.sol";
1313
import { LinkedList } from "../libraries/LinkedList.sol";
1414

1515
import { HorizonStakingBase } from "./HorizonStakingBase.sol";
16-
17-
/**
18-
* @title HorizonStaking contract
19-
* @notice The {HorizonStaking} contract allows service providers to stake and provision tokens to verifiers to be used
16+
e The {HorizonStaking} contract allows service providers to stake and provision tokens to verifiers to be used
2017
* as economic security for a service. It also allows delegators to delegate towards a service provider provision.
2118
* @dev Implements the {IHorizonStakingMain} interface.
2219
* @dev This is the main Staking contract in The Graph protocol after the Horizon upgrade.
@@ -361,7 +358,7 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
361358
) {
362359
tokensToWithdraw = delegation.__DEPRECATED_tokensLocked;
363360
}
364-
require(tokensToWithdraw > 0, "!tokens");
361+
require(tokensToWithdraw > 0, HorizonStakingNothingToWithdraw());
365362

366363
// Reset lock
367364
delegation.__DEPRECATED_tokensLocked = 0;
@@ -402,7 +399,7 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
402399
verifierDestination
403400
)
404401
);
405-
require(success, "Delegatecall: legacySlash failed");
402+
require(success, HorizonStakingLegacySlashFailed());
406403
return;
407404
}
408405

@@ -562,6 +559,15 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain {
562559
return _isAuthorized(serviceProvider, verifier, operator);
563560
}
564561

562+
/*
563+
* GETTERS
564+
*/
565+
566+
/// @inheritdoc IHorizonStakingMain
567+
function getStakingExtension() external view override returns (address) {
568+
return STAKING_EXTENSION_ADDRESS;
569+
}
570+
565571
/*
566572
* PRIVATE FUNCTIONS
567573
*/

packages/horizon/contracts/staking/HorizonStakingBase.sol

-8
Original file line numberDiff line numberDiff line change
@@ -50,14 +50,6 @@ abstract contract HorizonStakingBase is
5050
SUBGRAPH_DATA_SERVICE_ADDRESS = subgraphDataServiceAddress;
5151
}
5252

53-
/**
54-
* @notice Receive ETH into the Staking contract: this will always revert
55-
* @dev This function is only here to prevent ETH from being sent to the contract
56-
*/
57-
receive() external payable {
58-
revert("RECEIVE_ETH_NOT_ALLOWED");
59-
}
60-
6153
/// @inheritdoc IHorizonStakingBase
6254
/// @dev Removes deprecated fields from the return value.
6355
function getServiceProvider(address serviceProvider) external view override returns (ServiceProvider memory) {

packages/horizon/test/staking/delegation/legacyWithdraw.t.sol

+2-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,8 @@ contract HorizonStakingLegacyWithdrawDelegationTest is HorizonStakingTest {
9595
_setStorage_DelegationPool(users.indexer, 0, 0, 0);
9696
_setLegacyDelegation(users.indexer, users.delegator, 0, 0, 0);
9797

98-
vm.expectRevert("!tokens");
98+
bytes memory expectedError = abi.encodeWithSignature("HorizonStakingNothingToWithdraw()");
99+
vm.expectRevert(expectedError);
99100
staking.withdrawDelegated(users.indexer, address(0));
100101
}
101102
}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// SPDX-License-Identifier: MIT
2+
pragma solidity 0.8.27;
3+
4+
import "forge-std/Test.sol";
5+
6+
import { HorizonStakingTest } from "./HorizonStaking.t.sol";
7+
import { IGraphProxy } from "@graphprotocol/contracts/contracts/upgrades/IGraphProxy.sol";
8+
9+
contract HorizonStakingMiscTest is HorizonStakingTest {
10+
/*
11+
* TESTS
12+
*/
13+
14+
function test_ReceiveETH_Reverts() public {
15+
resetPrank(users.indexer);
16+
// GraphProxy implementation slot
17+
bytes32 implementationSlot = bytes32(uint256(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc));
18+
bytes32 bytesImplementationAddress = vm.load(address(staking), implementationSlot);
19+
console.log("storageValue", address(uint160(uint256(storageValue))));
20+
console.log("staking", address(staking));
21+
22+
bytes memory expectedError = abi.encodeWithSignature("HorizonStakingReceiveETHNotAllowed()");
23+
vm.expectRevert(expectedError);
24+
payable(address(uint160(uint256(storageValue)))).transfer(1 ether);
25+
}
26+
}

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

+27
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,31 @@ contract HorizonStakingLegacySlashTest is HorizonStakingTest {
204204
resetPrank(users.legacySlasher);
205205
_legacySlash(users.indexer, 1000 ether, 500 ether, makeAddr("fisherman"));
206206
}
207+
208+
function test_LegacySlash_WhenDelegateCallFails() public useIndexer useLegacySlasher(users.legacySlasher) {
209+
// Setup indexer with:
210+
// - tokensStaked = 1000 GRT
211+
// - tokensAllocated = 800 GRT
212+
// - tokensLocked = 300 GRT
213+
214+
_setIndexer(
215+
users.indexer,
216+
1000 ether, // tokensStaked
217+
800 ether, // tokensAllocated
218+
300 ether, // tokensLocked
219+
0 // tokensLockedUntil
220+
);
221+
222+
// Send tokens manually to staking
223+
token.transfer(address(staking), 1100 ether);
224+
225+
// Change staking extension code to an invalid opcode so the delegatecall reverts
226+
address stakingExtension = staking.getStakingExtension();
227+
vm.etch(stakingExtension, hex"fe");
228+
229+
resetPrank(users.legacySlasher);
230+
bytes memory expectedError = abi.encodeWithSignature("HorizonStakingLegacySlashFailed()");
231+
vm.expectRevert(expectedError);
232+
staking.slash(users.indexer, 1000 ether, 500 ether, makeAddr("fisherman"));
233+
}
207234
}

0 commit comments

Comments
 (0)