-
Notifications
You must be signed in to change notification settings - Fork 143
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: remove transfer tools from horizon (#1053)
* feat: remove transfer tools from horizon Signed-off-by: Tomás Migone <[email protected]> * fix: deprecate storage variables Signed-off-by: Tomás Migone <[email protected]> * fix: remove minimum delegation requirement from delegation flows Signed-off-by: Tomás Migone <[email protected]> * fix: minor changes Signed-off-by: Tomás Migone <[email protected]> --------- Signed-off-by: Tomás Migone <[email protected]>
- Loading branch information
Showing
19 changed files
with
30 additions
and
515 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,8 +22,7 @@ import { HorizonStakingBase } from "./HorizonStakingBase.sol"; | |
* It is designed to be deployed as an upgrade to the L2Staking contract from the legacy contracts package. | ||
* @dev It uses a {HorizonStakingExtension} contract to implement the full {IHorizonStaking} interface through delegatecalls. | ||
* This is due to the contract size limit on Arbitrum (24kB). The extension contract implements functionality to support | ||
* the legacy staking functions and the transfer tools. Both can be eventually removed without affecting the main | ||
* staking contract. | ||
* the legacy staking functions. It can be eventually removed without affecting the main staking contract. | ||
* @custom:security-contact Please email [email protected] if you find any | ||
* bugs. We may have an active bug bounty program. | ||
*/ | ||
|
@@ -42,11 +41,6 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain { | |
/// @dev Maximum number of simultaneous stake thaw requests (per provision) or undelegations (per delegation) | ||
uint256 private constant MAX_THAW_REQUESTS = 100; | ||
|
||
/// @dev Minimum amount of delegation to prevent rounding attacks. | ||
/// TODO: remove this after L2 transfer tool for delegation is removed | ||
/// (delegation on L2 has its own slippage protection) | ||
uint256 private constant MIN_DELEGATION = 1e18; | ||
|
||
/// @dev Address of the staking extension contract | ||
address private immutable STAKING_EXTENSION_ADDRESS; | ||
|
||
|
@@ -741,8 +735,6 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain { | |
* have been done before calling this function. | ||
*/ | ||
function _delegate(address _serviceProvider, address _verifier, uint256 _tokens, uint256 _minSharesOut) private { | ||
// TODO: remove this after L2 transfer tool for delegation is removed | ||
require(_tokens >= MIN_DELEGATION, HorizonStakingInsufficientTokens(_tokens, MIN_DELEGATION)); | ||
require( | ||
_provisions[_serviceProvider][_verifier].createdAt != 0, | ||
HorizonStakingInvalidProvision(_serviceProvider, _verifier) | ||
|
@@ -795,14 +787,6 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain { | |
pool.sharesThawing = pool.sharesThawing + thawingShares; | ||
|
||
delegation.shares = delegation.shares - _shares; | ||
// TODO: remove this when L2 transfer tools are removed | ||
if (delegation.shares != 0) { | ||
uint256 remainingTokens = (delegation.shares * (pool.tokens - pool.tokensThawing)) / pool.shares; | ||
require( | ||
remainingTokens >= MIN_DELEGATION, | ||
HorizonStakingInsufficientTokens(remainingTokens, MIN_DELEGATION) | ||
); | ||
} | ||
|
||
bytes32 thawRequestId = _createThawRequest( | ||
_serviceProvider, | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,9 +5,6 @@ pragma solidity 0.8.27; | |
import { ICuration } from "@graphprotocol/contracts/contracts/curation/ICuration.sol"; | ||
import { IGraphToken } from "@graphprotocol/contracts/contracts/token/IGraphToken.sol"; | ||
import { IHorizonStakingExtension } from "../interfaces/internal/IHorizonStakingExtension.sol"; | ||
import { IHorizonStakingTypes } from "../interfaces/internal/IHorizonStakingTypes.sol"; | ||
import { IL2StakingTypes } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingTypes.sol"; | ||
import { IL2StakingBase } from "@graphprotocol/contracts/contracts/l2/staking/IL2StakingBase.sol"; | ||
|
||
import { TokenUtils } from "@graphprotocol/contracts/contracts/utils/TokenUtils.sol"; | ||
import { MathUtils } from "../libraries/MathUtils.sol"; | ||
|
@@ -22,18 +19,15 @@ import { HorizonStakingBase } from "./HorizonStakingBase.sol"; | |
* to the Horizon Staking contract. It allows indexers to close allocations and collect pending query fees, but it | ||
* does not allow for the creation of new allocations. This should allow indexers to migrate to a subgraph data service | ||
* without losing rewards or having service interruptions. | ||
* @dev TODO: Once the transition period and the transfer tools are deemed not necessary this contract | ||
* can be removed. It's expected the transition period to last for a full allocation cycle (28 epochs). | ||
* @dev TODO: Once the transition period passes this contract can be removed. It's expected the transition period to | ||
* last for a full allocation cycle (28 epochs). | ||
* @custom:security-contact Please email [email protected] if you find any | ||
* bugs. We may have an active bug bounty program. | ||
*/ | ||
contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizonStakingExtension { | ||
contract HorizonStakingExtension is HorizonStakingBase, IHorizonStakingExtension { | ||
using TokenUtils for IGraphToken; | ||
using PPMMath for uint256; | ||
|
||
/// @dev Minimum amount of tokens that can be delegated | ||
uint256 private constant MINIMUM_DELEGATION = 1e18; | ||
|
||
/** | ||
* @dev Checks that the sender is the L2GraphTokenGateway as configured on the Controller. | ||
*/ | ||
|
@@ -53,52 +47,6 @@ contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizon | |
address subgraphDataServiceAddress | ||
) HorizonStakingBase(controller, subgraphDataServiceAddress) {} | ||
|
||
/** | ||
* @notice Receive tokens with a callhook from the bridge. | ||
* @dev The encoded _data can contain information about an service provider's stake | ||
* or a delegator's delegation. | ||
* See L1MessageCodes in IL2Staking for the supported messages. | ||
* @dev "indexer" in this context refers to a service provider (legacy terminology for the bridge) | ||
* @param from Token sender in L1 | ||
* @param tokens Amount of tokens that were transferred | ||
* @param data ABI-encoded callhook data which must include a uint8 code and either a ReceiveIndexerStakeData or ReceiveDelegationData struct. | ||
*/ | ||
function onTokenTransfer( | ||
address from, | ||
uint256 tokens, | ||
bytes calldata data | ||
) external override notPaused onlyL2Gateway { | ||
require(from == _counterpartStakingAddress, "ONLY_L1_STAKING_THROUGH_BRIDGE"); | ||
(uint8 code, bytes memory functionData) = abi.decode(data, (uint8, bytes)); | ||
|
||
if (code == uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_INDEXER_STAKE_CODE)) { | ||
IL2StakingTypes.ReceiveIndexerStakeData memory indexerData = abi.decode( | ||
functionData, | ||
(IL2StakingTypes.ReceiveIndexerStakeData) | ||
); | ||
_receiveIndexerStake(tokens, indexerData); | ||
} else if (code == uint8(IL2StakingTypes.L1MessageCodes.RECEIVE_DELEGATION_CODE)) { | ||
IL2StakingTypes.ReceiveDelegationData memory delegationData = abi.decode( | ||
functionData, | ||
(IL2StakingTypes.ReceiveDelegationData) | ||
); | ||
_receiveDelegation(tokens, delegationData); | ||
} else { | ||
revert("INVALID_CODE"); | ||
} | ||
} | ||
|
||
/** | ||
* @notice Set the address of the counterpart (L1 or L2) staking contract. | ||
* @dev This function can only be called by the governor. | ||
* TODO: Remove after L2 transition period | ||
* @param counterpart Address of the counterpart staking contract in the other chain, without any aliasing. | ||
*/ | ||
function setCounterpartStakingAddress(address counterpart) external override onlyGovernor { | ||
_counterpartStakingAddress = counterpart; | ||
emit CounterpartStakingAddressSet(counterpart); | ||
} | ||
|
||
/** | ||
* @notice Close an allocation and free the staked tokens. | ||
* To be eligible for rewards a proof of indexing must be presented. | ||
|
@@ -318,71 +266,6 @@ contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizon | |
return _legacyOperatorAuth[serviceProvider][operator]; | ||
} | ||
|
||
/** | ||
* @dev Receive an Indexer's stake from L1. | ||
* The specified amount is added to the indexer's stake; the indexer's | ||
* address is specified in the _indexerData struct. | ||
* @param _tokens Amount of tokens that were transferred | ||
* @param _indexerData struct containing the indexer's address | ||
*/ | ||
function _receiveIndexerStake( | ||
uint256 _tokens, | ||
IL2StakingTypes.ReceiveIndexerStakeData memory _indexerData | ||
) private { | ||
address indexer = _indexerData.indexer; | ||
// Deposit tokens into the indexer stake | ||
_stake(indexer, _tokens); | ||
} | ||
|
||
/** | ||
* @dev Receive a Delegator's delegation from L1. | ||
* The specified amount is added to the delegator's delegation; the delegator's | ||
* address and the indexer's address are specified in the _delegationData struct. | ||
* Note that no delegation tax is applied here. | ||
* @dev Note that L1 staking contract only allows delegation transfer if the indexer has already transferred, | ||
* this means the corresponding delegation pool exists. | ||
* @param _tokens Amount of tokens that were transferred | ||
* @param _delegationData struct containing the delegator's address and the indexer's address | ||
*/ | ||
function _receiveDelegation( | ||
uint256 _tokens, | ||
IL2StakingTypes.ReceiveDelegationData memory _delegationData | ||
) private { | ||
require(_provisions[_delegationData.indexer][SUBGRAPH_DATA_SERVICE_ADDRESS].createdAt != 0, "!provision"); | ||
// Get the delegation pool of the indexer | ||
DelegationPoolInternal storage pool = _legacyDelegationPools[_delegationData.indexer]; | ||
IHorizonStakingTypes.DelegationInternal storage delegation = pool.delegators[_delegationData.delegator]; | ||
|
||
// If pool is in an invalid state, return the tokens to the delegator | ||
if (pool.tokens == 0 && (pool.shares != 0 || pool.sharesThawing != 0)) { | ||
_graphToken().transfer(_delegationData.delegator, _tokens); | ||
emit TransferredDelegationReturnedToDelegator(_delegationData.indexer, _delegationData.delegator, _tokens); | ||
return; | ||
} | ||
|
||
// Calculate shares to issue (without applying any delegation tax) | ||
uint256 shares = (pool.tokens == 0 || pool.tokens == pool.tokensThawing) | ||
? _tokens | ||
: ((_tokens * pool.shares) / (pool.tokens - pool.tokensThawing)); | ||
|
||
if (shares == 0 || _tokens < MINIMUM_DELEGATION) { | ||
// If no shares would be issued (probably a rounding issue or attack), | ||
// or if the amount is under the minimum delegation (which could be part of a rounding attack), | ||
// return the tokens to the delegator | ||
_graphToken().pushTokens(_delegationData.delegator, _tokens); | ||
emit TransferredDelegationReturnedToDelegator(_delegationData.indexer, _delegationData.delegator, _tokens); | ||
} else { | ||
// Update the delegation pool | ||
pool.tokens = pool.tokens + _tokens; | ||
pool.shares = pool.shares + shares; | ||
|
||
// Update the individual delegation | ||
delegation.shares = delegation.shares + shares; | ||
|
||
emit StakeDelegated(_delegationData.indexer, _delegationData.delegator, _tokens, shares); | ||
} | ||
} | ||
|
||
/** | ||
* @dev Collect tax to burn for an amount of tokens. | ||
* @param _tokens Total tokens received used to calculate the amount of tax to collect | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.