Skip to content

Commit

Permalink
chore: use PPMMath
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 23, 2024
1 parent ac5546d commit 187daaf
Show file tree
Hide file tree
Showing 5 changed files with 24 additions and 32 deletions.
6 changes: 3 additions & 3 deletions packages/horizon/contracts/data-service/GraphDirectory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@ abstract contract GraphDirectory {
ICuration graphCuration
);

error GraphDirectoryInvalidZeroAddress();
error GraphDirectoryInvalidZeroAddress(bytes contractName);

constructor(address controller) {
if (controller == address(0)) {
revert GraphDirectoryInvalidZeroAddress();
revert GraphDirectoryInvalidZeroAddress("Controller");
}
GRAPH_CONTROLLER = IController(controller);

Expand Down Expand Up @@ -137,7 +137,7 @@ abstract contract GraphDirectory {
function _getContractFromController(bytes memory _contractName) private view returns (address) {
address contractAddress = GRAPH_CONTROLLER.getContractProxy(keccak256(_contractName));
if (contractAddress == address(0)) {
revert GraphDirectoryInvalidZeroAddress();
revert GraphDirectoryInvalidZeroAddress(_contractName);
}
return contractAddress;
}
Expand Down
8 changes: 4 additions & 4 deletions packages/horizon/contracts/libraries/PPMMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.24;

library PPMMath {
/// @notice Maximum value in parts per million (PPM).
/// @notice Maximum value (100%) in parts per million (PPM).
uint256 internal constant MAX_PPM = 1_000_000;

error PPMMathInvalidPPM(uint256 ppm);
Expand All @@ -20,10 +20,10 @@ library PPMMath {
// to prevent curation fees from rounding down to zero
// a must be in ppm
function mulPPMRoundUp(uint256 a, uint256 b) internal pure returns (uint256) {
if (!isValidPPM(a)) {
revert PPMMathInvalidPPM(a);
if (!isValidPPM(b)) {
revert PPMMathInvalidPPM(b);
}
return b - mulPPM(MAX_PPM - a, b);
return a - mulPPM(MAX_PPM - b, a);
}

function isValidPPM(uint256 ppm) internal pure returns (bool) {
Expand Down
8 changes: 4 additions & 4 deletions packages/horizon/contracts/payments/GraphPayments.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import { IGraphPayments } from "../interfaces/IGraphPayments.sol";

import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol";
import { TokenUtils } from "../libraries/TokenUtils.sol";
import { PPMMath } from "../libraries/PPMMath.sol";

import { GraphDirectory } from "../data-service/GraphDirectory.sol";
import { GraphPaymentsStorageV1Storage } from "./GraphPaymentsStorage.sol";

contract GraphPayments is Multicall, GraphDirectory, GraphPaymentsStorageV1Storage, IGraphPayments {
using TokenUtils for IGraphToken;

uint256 private immutable MAX_PPM = 1000000; // 100% in parts per million
using PPMMath for uint256;

event GraphPaymentsCollected(
address indexed sender,
Expand Down Expand Up @@ -55,9 +55,9 @@ contract GraphPayments is Multicall, GraphDirectory, GraphPaymentsStorageV1Stora
_graphToken().pullTokens(msg.sender, tokens);

// Calculate cuts
uint256 tokensProtocol = (tokens * PROTOCOL_PAYMENT_CUT) / MAX_PPM;
uint256 tokensProtocol = tokens.mulPPM(PROTOCOL_PAYMENT_CUT);
uint256 delegationFeeCut = _graphStaking().getDelegationFeeCut(receiver, dataService, uint8(paymentType));
uint256 tokensDelegationPool = (tokens * delegationFeeCut) / MAX_PPM;
uint256 tokensDelegationPool = tokens.mulPPM(delegationFeeCut);
uint256 totalCut = tokensProtocol + tokensDataService + tokensDelegationPool;
if (totalCut > tokens) {
revert GraphPaymentsInsufficientTokens(tokens, totalCut);
Expand Down
11 changes: 5 additions & 6 deletions packages/horizon/contracts/staking/HorizonStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { GraphUpgradeable } from "@graphprotocol/contracts/contracts/upgrades/Gr
import { IHorizonStakingBase } from "../interfaces/IHorizonStakingBase.sol";
import { IGraphToken } from "../interfaces/IGraphToken.sol";

import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol";
import { TokenUtils } from "../libraries/TokenUtils.sol";
import { MathUtils } from "../libraries/MathUtils.sol";
import { Multicall } from "@openzeppelin/contracts/utils/Multicall.sol";
import { PPMMath } from "../libraries/PPMMath.sol";

import { Managed } from "./utilities/Managed.sol";
import { HorizonStakingV1Storage } from "./HorizonStakingStorage.sol";
Expand All @@ -23,13 +24,11 @@ import { HorizonStakingV1Storage } from "./HorizonStakingStorage.sol";
*/
contract HorizonStaking is GraphUpgradeable, Multicall, Managed, HorizonStakingV1Storage, IHorizonStakingBase {
using TokenUtils for IGraphToken;

/// @dev 100% in parts per million
uint32 internal constant MAX_PPM = 1000000;
using PPMMath for uint256;

/// @dev Maximum value that can be set as the maxVerifierCut in a provision.
/// It is equivalent to 100% in parts-per-million
uint32 private constant MAX_MAX_VERIFIER_CUT = 1000000; // 100%
uint32 private constant MAX_MAX_VERIFIER_CUT = uint32(PPMMath.MAX_PPM);

/// @dev Minimum size of a provision
uint256 private constant MIN_PROVISION_SIZE = 1e18;
Expand Down Expand Up @@ -393,7 +392,7 @@ contract HorizonStaking is GraphUpgradeable, Multicall, Managed, HorizonStakingV
uint256 tokensToSlash = tokens;
uint256 providerTokensSlashed = MathUtils.min(prov.tokens, tokensToSlash);
if (providerTokensSlashed > 0) {
require((prov.tokens * prov.maxVerifierCut) / MAX_PPM >= tokensVerifier, "verifier cut too high");
require(prov.tokens.mulPPM(prov.maxVerifierCut) >= tokensVerifier, "verifier cut too high");
if (tokensVerifier > 0) {
_graphToken().pushTokens(verifierDestination, tokensVerifier);
emit VerifierTokensSent(serviceProvider, verifier, verifierDestination, tokensVerifier);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { IHorizonStakingTypes } from "../interfaces/IHorizonStakingTypes.sol";
import { TokenUtils } from "../libraries/TokenUtils.sol";
import { MathUtils } from "../libraries/MathUtils.sol";
import { ExponentialRebates } from "./libraries/ExponentialRebates.sol";
import { PPMMath } from "../libraries/PPMMath.sol";

import { Managed } from "./utilities/Managed.sol";
import { HorizonStakingV1Storage } from "./HorizonStakingStorage.sol";
Expand All @@ -32,9 +33,7 @@ abstract contract StakingBackwardsCompatibility is
IStakingBackwardsCompatibility
{
using TokenUtils for IGraphToken;

/// @dev 100% in parts per million
uint32 internal constant MAX_PPM = 1000000;
using PPMMath for uint256;

address public immutable SUBGRAPH_DATA_SERVICE_ADDRESS;

Expand Down Expand Up @@ -272,10 +271,7 @@ abstract contract StakingBackwardsCompatibility is
* @return Amount of tax charged
*/
function _collectTax(uint256 _tokens, uint256 _percentage) private returns (uint256) {
// Calculate tokens after tax first, and subtract that,
// to prevent the tax from rounding down to zero
uint256 tokensAfterTax = ((uint256(MAX_PPM) - _percentage) * _tokens) / MAX_PPM;
uint256 tax = _tokens - tokensAfterTax;
uint256 tax = _tokens.mulPPMRoundUp(_percentage);
_graphToken().burnTokens(tax); // Burn tax if any
return tax;
}
Expand Down Expand Up @@ -404,8 +400,8 @@ abstract contract StakingBackwardsCompatibility is
function _collectDelegationQueryRewards(address _indexer, uint256 _tokens) private returns (uint256) {
uint256 delegationRewards = 0;
IHorizonStakingTypes.DelegationPoolInternal storage pool = _legacyDelegationPools[_indexer];
if (pool.tokens > 0 && pool.__DEPRECATED_queryFeeCut < MAX_PPM) {
uint256 indexerCut = (uint256(pool.__DEPRECATED_queryFeeCut) * _tokens) / MAX_PPM;
if (pool.tokens > 0 && uint256(pool.__DEPRECATED_queryFeeCut).isValidPPM()) {
uint256 indexerCut = uint256(pool.__DEPRECATED_queryFeeCut).mulPPM(_tokens);
delegationRewards = _tokens - indexerCut;
pool.tokens = pool.tokens + delegationRewards;
}
Expand All @@ -422,8 +418,8 @@ abstract contract StakingBackwardsCompatibility is
function _collectDelegationIndexingRewards(address _indexer, uint256 _tokens) private returns (uint256) {
uint256 delegationRewards = 0;
IHorizonStakingTypes.DelegationPoolInternal storage pool = _legacyDelegationPools[_indexer];
if (pool.tokens > 0 && pool.__DEPRECATED_indexingRewardCut < MAX_PPM) {
uint256 indexerCut = (uint256(pool.__DEPRECATED_indexingRewardCut) * _tokens) / MAX_PPM;
if (pool.tokens > 0 && uint256(pool.__DEPRECATED_indexingRewardCut).isValidPPM()) {
uint256 indexerCut = uint256(pool.__DEPRECATED_indexingRewardCut).mulPPM(_tokens);
delegationRewards = _tokens - indexerCut;
pool.tokens = pool.tokens + delegationRewards;
}
Expand Down Expand Up @@ -451,10 +447,7 @@ abstract contract StakingBackwardsCompatibility is
bool isCurationEnabled = _curationCut > 0 && address(curation) != address(0);

if (isCurationEnabled && curation.isCurated(_subgraphDeploymentID)) {
// Calculate the tokens after curation fees first, and subtact that,
// to prevent curation fees from rounding down to zero
uint256 tokensAfterCurationFees = ((uint256(MAX_PPM) - _curationCut) * _tokens) / MAX_PPM;
uint256 curationFees = _tokens - tokensAfterCurationFees;
uint256 curationFees = _tokens.mulPPMRoundUp(_curationCut);
if (curationFees > 0) {
// Transfer and call collect()
// This function transfer tokens to a trusted protocol contracts
Expand Down

0 comments on commit 187daaf

Please sign in to comment.