Skip to content

Commit

Permalink
docs: add natspec to data service fees extension contract
Browse files Browse the repository at this point in the history
Signed-off-by: Tomás Migone <[email protected]>
  • Loading branch information
tmigone committed May 29, 2024
1 parent c3fe9d1 commit 75d3cb9
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 23 deletions.
2 changes: 1 addition & 1 deletion packages/horizon/contracts/data-service/DataService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { ProvisionManager } from "./utilities/ProvisionManager.sol";
* - GraphDirectory, allows the data service to interact with Graph Horizon contracts
* - ProvisionManager, provides functionality to manage provisions
*
* The derived contract should add functionality that implements the interfaces described in {IDataService}.
* The derived contract MUST implement all the interfaces described in {IDataService}.
*/
abstract contract DataService is GraphDirectory, ProvisionManager, DataServiceV1Storage, IDataService {
/**
Expand Down
95 changes: 91 additions & 4 deletions packages/horizon/contracts/data-service/GraphDirectory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,59 @@ import { ICuration } from "@graphprotocol/contracts/contracts/curation/ICuration
* and uses immutable variables to minimize gas costs.
*/
abstract contract GraphDirectory {
// Graph Horizon contracts
// -- Graph Horizon contracts --

/// @dev The Graph Token contract address
IGraphToken private immutable GRAPH_TOKEN;

/// @dev The Horizon Staking contract address
IHorizonStaking private immutable GRAPH_STAKING;

/// @dev The Graph Payments contract address
IGraphPayments private immutable GRAPH_PAYMENTS;

/// @dev The Payments Escrow contract address
IPaymentsEscrow private immutable GRAPH_PAYMENTS_ESCROW;

// Graph periphery contracts
// -- Graph periphery contracts --

/// @dev The Graph Controller contract address
IController private immutable GRAPH_CONTROLLER;

/// @dev The Epoch Manager contract address
IEpochManager private immutable GRAPH_EPOCH_MANAGER;

/// @dev The Rewards Manager contract address
IRewardsManager private immutable GRAPH_REWARDS_MANAGER;

/// @dev The Token Gateway contract address
ITokenGateway private immutable GRAPH_TOKEN_GATEWAY;

/// @dev The Bridge Escrow contract address
IBridgeEscrow private immutable GRAPH_BRIDGE_ESCROW;

/// @dev The Graph Proxy Admin contract address
IGraphProxyAdmin private immutable GRAPH_PROXY_ADMIN;

// Legacy Graph contracts - required for StakingBackwardCompatibility
// TODO: remove these once StakingBackwardCompatibility is removed
// -- Legacy Graph contracts --
// These are required for backwards compatibility on HorizonStakingExtension
// TODO: remove these once HorizonStakingExtension is removed
ICuration private immutable GRAPH_CURATION;

/**
* @notice Emitted when the GraphDirectory is initialized
* @param graphToken The Graph Token contract address
* @param graphStaking The Horizon Staking contract address
* @param graphPayments The Graph Payments contract address
* @param graphEscrow The Payments Escrow contract address
* @param graphController The Graph Controller contract address
* @param graphEpochManager The Epoch Manager contract address
* @param graphRewardsManager The Rewards Manager contract address
* @param graphTokenGateway The Token Gateway contract address
* @param graphBridgeEscrow The Bridge Escrow contract address
* @param graphProxyAdmin The Graph Proxy Admin contract address
* @param graphCuration The Curation contract address
*/
event GraphDirectoryInitialized(
IGraphToken indexed graphToken,
IHorizonStaking indexed graphStaking,
Expand All @@ -56,8 +91,21 @@ abstract contract GraphDirectory {
ICuration graphCuration
);

/**
* @notice Thrown when either the controller is the zero address or a contract address is not found
* on the controller
* @param contractName The name of the contract that was not found, or the controller
*/
error GraphDirectoryInvalidZeroAddress(bytes contractName);

/**
* @notice Constructor for the GraphDirectory contract
* @dev Requirements:
* - `controller` cannot be zero address
*
* Emits a {GraphDirectoryInitialized} event
* @param controller The address of the Graph Controller contract.
*/
constructor(address controller) {
require(controller != address(0), GraphDirectoryInvalidZeroAddress("Controller"));

Expand Down Expand Up @@ -88,50 +136,89 @@ abstract contract GraphDirectory {
);
}

/**
* @notice Get the Graph Token contract
*/
function _graphToken() internal view returns (IGraphToken) {
return GRAPH_TOKEN;
}

/**
* @notice Get the Horizon Staking contract
*/
function _graphStaking() internal view returns (IHorizonStaking) {
return GRAPH_STAKING;
}

/**
* @notice Get the Graph Payments contract
*/
function _graphPayments() internal view returns (IGraphPayments) {
return GRAPH_PAYMENTS;
}

/**
* @notice Get the Payments Escrow contract
*/
function _graphPaymentsEscrow() internal view returns (IPaymentsEscrow) {
return GRAPH_PAYMENTS_ESCROW;
}

/**
* @notice Get the Graph Controller contract
*/
function _graphController() internal view returns (IController) {
return GRAPH_CONTROLLER;
}

/**
* @notice Get the Epoch Manager contract
*/
function _graphEpochManager() internal view returns (IEpochManager) {
return GRAPH_EPOCH_MANAGER;
}

/**
* @notice Get the Rewards Manager contract
*/
function _graphRewardsManager() internal view returns (IRewardsManager) {
return GRAPH_REWARDS_MANAGER;
}

/**
* @notice Get the Graph Token Gateway contract
*/
function _graphTokenGateway() internal view returns (ITokenGateway) {
return GRAPH_TOKEN_GATEWAY;
}

/**
* @notice Get the Bridge Escrow contract
*/
function _graphBridgeEscrow() internal view returns (IBridgeEscrow) {
return GRAPH_BRIDGE_ESCROW;
}

/**
* @notice Get the Graph Proxy Admin contract
*/
function _graphProxyAdmin() internal view returns (IGraphProxyAdmin) {
return GRAPH_PROXY_ADMIN;
}

/**
* @notice Get the Curation contract
*/
function _graphCuration() internal view returns (ICuration) {
return GRAPH_CURATION;
}

/**
* @notice Get a contract address from the controller
* @dev Requirements:
* - The `_contractName` must be registered in the controller
* @param _contractName The name of the contract to fetch from the controller
*/
function _getContractFromController(bytes memory _contractName) private view returns (address) {
address contractAddress = GRAPH_CONTROLLER.getContractProxy(keccak256(_contractName));
require(contractAddress != address(0), GraphDirectoryInvalidZeroAddress(_contractName));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,35 @@ import { LinkedList } from "../../libraries/LinkedList.sol";
import { DataService } from "../DataService.sol";
import { DataServiceFeesV1Storage } from "./DataServiceFeesStorage.sol";

/**
* @title DataServiceFees
* @notice Adds functionality to the {IDataService} contract to handle payment collateralization
* using a Horizon provision. See {IDataServiceFees} for more details.
* @dev Implementation of the {IDataServiceFees} interface.
*/
abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDataServiceFees {
using ProvisionTracker for mapping(address => uint256);
using LinkedList for LinkedList.List;

/**
* @notice See {IDataServiceFees-releaseStake}
*/
function releaseStake(uint256 n) external virtual {
_releaseStake(msg.sender, n);
}

/**
* @notice Locks stake for a service provider to back a payment.
* Creates a stake claim, which is stored in a linked list by service provider.
* @dev Requirements:
* - The associated provision must have enough available tokens to lock the stake.
*
* Emits a {StakeClaimLocked} event.
*
* @param _serviceProvider The address of the service provider
* @param _tokens The amount of tokens to lock in the claim
* @param _unlockTimestamp The timestamp when the tokens can be released
*/
function _lockStake(address _serviceProvider, uint256 _tokens, uint256 _unlockTimestamp) internal {
feesProvisionTracker.lock(_graphStaking(), _serviceProvider, _tokens, maximumDelegationRatio);

Expand All @@ -25,7 +46,6 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
// Save item and add to list
bytes32 claimId = _buildStakeClaimId(_serviceProvider, claimsList.nonce);
claims[claimId] = StakeClaim({
serviceProvider: _serviceProvider,
tokens: _tokens,
createdAt: block.timestamp,
releaseAt: _unlockTimestamp,
Expand All @@ -37,8 +57,9 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
emit StakeClaimLocked(_serviceProvider, claimId, _tokens, _unlockTimestamp);
}

/// @notice Release expired stake claims for a service provider
/// @param _n The number of stake claims to release, or 0 to release all
/**
* @notice See {IDataServiceFees-releaseStake}
*/
function _releaseStake(address _serviceProvider, uint256 _n) internal {
LinkedList.List storage claimsList = claimsLists[_serviceProvider];
(uint256 claimsReleased, bytes memory data) = claimsList.traverse(
Expand All @@ -52,6 +73,15 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
emit StakeClaimsReleased(_serviceProvider, claimsReleased, abi.decode(data, (uint256)));
}

/**
* @notice Processes a stake claim, releasing the tokens if the claim has expired.
* @dev This function is used as a callback in the stake claims linked list traversal.
* @param _claimId The id of the stake claim
* @param _acc The accumulator for the stake claims being processed
* @return Wether the stake claim is still locked, indicating that the traversal should continue or stop.
* @return Wether the stake claim should be deleted
* @return The updated accumulator data
*/
function _processStakeClaim(bytes32 _claimId, bytes memory _acc) private returns (bool, bool, bytes memory) {
StakeClaim memory claim = _getStakeClaim(_claimId);

Expand All @@ -72,22 +102,41 @@ abstract contract DataServiceFees is DataService, DataServiceFeesV1Storage, IDat
return (false, true, _acc);
}

/**
* @notice Deletes a stake claim.
* @dev This function is used as a callback in the stake claims linked list traversal.
* @param _claimId The ID of the stake claim to delete
*/
function _deleteStakeClaim(bytes32 _claimId) private {
delete claims[_claimId];
}

/**
* @notice Gets the details of a stake claim
* @param _claimId The ID of the stake claim
*/
function _getStakeClaim(bytes32 _claimId) private view returns (StakeClaim memory) {
StakeClaim memory claim = claims[_claimId];
require(claim.createdAt != 0, DataServiceFeesClaimNotFound(_claimId));
return claim;
}

/**
* @notice Gets the next stake claim in the linked list
* @dev This function is used as a callback in the stake claims linked list traversal.
* @param _claimId The ID of the stake claim
*/
function _getNextStakeClaim(bytes32 _claimId) private view returns (bytes32) {
StakeClaim memory claim = claims[_claimId];
require(claim.createdAt != 0, DataServiceFeesClaimNotFound(_claimId));
return claim.nextClaim;
}

/**
* @notice Builds a stake claim ID
* @param _serviceProvider The address of the service provider
* @param _nonce A nonce of the stake claim
*/
function _buildStakeClaimId(address _serviceProvider, uint256 _nonce) private view returns (bytes32) {
return keccak256(abi.encodePacked(address(this), _serviceProvider, _nonce));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@ import { IGraphPayments } from "../../interfaces/IGraphPayments.sol";

/**
* @title Interface of the base {DataService} contract as defined by the Graph Horizon specification.
* @dev This interface provides a guardrail for data service implementations that utilize Graph Horizon.
* @notice This interface provides a guardrail for data service implementations that utilize Graph Horizon.
* It's expected that implementations follow the specification however much of it is intentionally loose
* to allow for greater flexibility when designing a data service. For specifics always check the data
* service implementation.
* In general, this is a great starting point for data services that want to use Graph Horizon
* to provide economic security for a service being provided. It assumes two main forms of retribution for
* service providers:
* - service payment, to compensate ongoing work required to serve customers requests
* - service fees, earnt by serving customer requests, ideally leveraging {GraphPayments} to collect fees from the payer
*
* TIP: TODO: link to data service framework documentation
* to provide economic security for a service being provided.
*/
interface IDataService {
/**
Expand Down
Loading

0 comments on commit 75d3cb9

Please sign in to comment.