Transaction Hash:
Block:
16219728 at Dec-19-2022 03:46:11 PM +UTC
Transaction Fee:
0.001735920976259526 ETH
$3.97
Gas Used:
119,021 Gas / 14.584997406 Gwei
Emitted Events:
195 |
GraphProxy.0x1b2e7737e043c5cf1b587ceb4daeb7ae00148b9bda8f79f1093eead08f141952( 0x1b2e7737e043c5cf1b587ceb4daeb7ae00148b9bda8f79f1093eead08f141952, 0x0000000000000000000000005a8904be09625965d9aec4bffd30d853438a053e, 0x0000000000000000000000000ab0f1575bc24b69afb3d8be42802b6f6f257f57, 000000000000000000000000000000000000000000001d380afa5bac301486c3 )
|
196 |
GraphToken.Transfer( from=GraphProxy, to=[Receiver] GraphTokenLockWallet, value=137982436716827102185155 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0x95e8403E...f62EFBaDd
Miner
| (Fee Recipient: 0x95...aDd) | 2.702949605112521305 Eth | 2.703128136612521305 Eth | 0.0001785315 | |
0xA70D2B72...637b5b167 |
0.040499544158068089 Eth
Nonce: 169
|
0.038763623181808563 Eth
Nonce: 170
| 0.001735920976259526 | ||
0xc944E90C...05Cda44a7 | |||||
0xF55041E3...9B01263b9 | (The Graph: Proxy 2) |
Execution Trace
GraphTokenLockWallet.51a60b02( )
GraphTokenLockWallet.51a60b02( )
-
GraphTokenLockManager.getAuthFunctionCallTarget( _sigHash=System.Byte[] ) => ( 0xF55041E37E12cD407ad00CE2910B8269B01263b9 )
-
GraphToken.balanceOf( account=0x0Ab0F1575Bc24B69aFB3d8bE42802B6F6f257F57 ) => ( 0 )
GraphProxy.51a60b02( )
Staking.withdrawDelegated( _indexer=0x5A8904be09625965d9AEc4BFfD30D853438a053e, _delegateToIndexer=0x0000000000000000000000000000000000000000 ) => ( 137982436716827102185155 )
-
Controller.STATICCALL( )
GraphProxy.STATICCALL( )
-
EpochManager.DELEGATECALL( )
-
-
GraphToken.transfer( recipient=0x0Ab0F1575Bc24B69aFB3d8bE42802B6F6f257F57, amount=137982436716827102185155 ) => ( True )
-
-
File 1 of 9: GraphTokenLockWallet
File 2 of 9: GraphProxy
File 3 of 9: GraphToken
File 4 of 9: GraphTokenLockWallet
File 5 of 9: GraphTokenLockManager
File 6 of 9: Staking
File 7 of 9: Controller
File 8 of 9: GraphProxy
File 9 of 9: EpochManager
// SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./GraphTokenLock.sol"; import "./IGraphTokenLockManager.sol"; /** * @title GraphTokenLockWallet * @notice This contract is built on top of the base GraphTokenLock functionality. * It allows wallet beneficiaries to use the deposited funds to perform specific function calls * on specific contracts. * * The idea is that supporters with locked tokens can participate in the protocol * but disallow any release before the vesting/lock schedule. * The beneficiary can issue authorized function calls to this contract that will * get forwarded to a target contract. A target contract is any of our protocol contracts. * The function calls allowed are queried to the GraphTokenLockManager, this way * the same configuration can be shared for all the created lock wallet contracts. * * NOTE: Contracts used as target must have its function signatures checked to avoid collisions * with any of this contract functions. * Beneficiaries need to approve the use of the tokens to the protocol contracts. For convenience * the maximum amount of tokens is authorized. */ contract GraphTokenLockWallet is GraphTokenLock { using SafeMath for uint256; using SafeERC20 for IERC20; // -- State -- IGraphTokenLockManager public manager; uint256 public usedAmount; uint256 private constant MAX_UINT256 = 2**256 - 1; // -- Events -- event ManagerUpdated(address indexed _oldManager, address indexed _newManager); event TokenDestinationsApproved(); event TokenDestinationsRevoked(); // Initializer function initialize( address _manager, address _owner, address _beneficiary, address _token, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, Revocability _revocable ) external { _initialize( _owner, _beneficiary, _token, _managedAmount, _startTime, _endTime, _periods, _releaseStartTime, _vestingCliffTime, _revocable ); _setManager(_manager); } // -- Admin -- /** * @notice Sets a new manager for this contract * @param _newManager Address of the new manager */ function setManager(address _newManager) external onlyOwner { _setManager(_newManager); } /** * @dev Sets a new manager for this contract * @param _newManager Address of the new manager */ function _setManager(address _newManager) private { require(_newManager != address(0), "Manager cannot be empty"); require(Address.isContract(_newManager), "Manager must be a contract"); address oldManager = address(manager); manager = IGraphTokenLockManager(_newManager); emit ManagerUpdated(oldManager, _newManager); } // -- Beneficiary -- /** * @notice Approves protocol access of the tokens managed by this contract * @dev Approves all token destinations registered in the manager to pull tokens */ function approveProtocol() external onlyBeneficiary { address[] memory dstList = manager.getTokenDestinations(); for (uint256 i = 0; i < dstList.length; i++) { token.safeApprove(dstList[i], MAX_UINT256); } emit TokenDestinationsApproved(); } /** * @notice Revokes protocol access of the tokens managed by this contract * @dev Revokes approval to all token destinations in the manager to pull tokens */ function revokeProtocol() external onlyBeneficiary { address[] memory dstList = manager.getTokenDestinations(); for (uint256 i = 0; i < dstList.length; i++) { token.safeApprove(dstList[i], 0); } emit TokenDestinationsRevoked(); } /** * @notice Forward authorized contract calls to protocol contracts * @dev Fallback function can be called by the beneficiary only if function call is allowed */ fallback() external payable { // Only beneficiary can forward calls require(msg.sender == beneficiary, "Unauthorized caller"); // Function call validation address _target = manager.getAuthFunctionCallTarget(msg.sig); require(_target != address(0), "Unauthorized function"); uint256 oldBalance = currentBalance(); // Call function with data Address.functionCall(_target, msg.data); // Tracked used tokens in the protocol // We do this check after balances were updated by the forwarded call // Check is only enforced for revocable contracts to save some gas if (revocable == Revocability.Enabled) { // Track contract balance change uint256 newBalance = currentBalance(); if (newBalance < oldBalance) { // Outflow uint256 diff = oldBalance.sub(newBalance); usedAmount = usedAmount.add(diff); } else { // Inflow: We can receive profits from the protocol, that could make usedAmount to // underflow. We set it to zero in that case. uint256 diff = newBalance.sub(oldBalance); usedAmount = (diff >= usedAmount) ? 0 : usedAmount.sub(diff); } require(usedAmount <= vestedAmount(), "Cannot use more tokens than vested amount"); } } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./Ownable.sol"; import "./MathUtils.sol"; import "./IGraphTokenLock.sol"; /** * @title GraphTokenLock * @notice Contract that manages an unlocking schedule of tokens. * @dev The contract lock manage a number of tokens deposited into the contract to ensure that * they can only be released under certain time conditions. * * This contract implements a release scheduled based on periods and tokens are released in steps * after each period ends. It can be configured with one period in which case it is like a plain TimeLock. * It also supports revocation to be used for vesting schedules. * * The contract supports receiving extra funds than the managed tokens ones that can be * withdrawn by the beneficiary at any time. * * A releaseStartTime parameter is included to override the default release schedule and * perform the first release on the configured time. After that it will continue with the * default schedule. */ abstract contract GraphTokenLock is Ownable, IGraphTokenLock { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 private constant MIN_PERIOD = 1; // -- State -- IERC20 public token; address public beneficiary; // Configuration // Amount of tokens managed by the contract schedule uint256 public managedAmount; uint256 public startTime; // Start datetime (in unixtimestamp) uint256 public endTime; // Datetime after all funds are fully vested/unlocked (in unixtimestamp) uint256 public periods; // Number of vesting/release periods // First release date for tokens (in unixtimestamp) // If set, no tokens will be released before releaseStartTime ignoring // the amount to release each period uint256 public releaseStartTime; // A cliff set a date to which a beneficiary needs to get to vest // all preceding periods uint256 public vestingCliffTime; Revocability public revocable; // Whether to use vesting for locked funds // State bool public isRevoked; bool public isInitialized; uint256 public releasedAmount; // -- Events -- event TokensReleased(address indexed beneficiary, uint256 amount); event TokensWithdrawn(address indexed beneficiary, uint256 amount); event TokensRevoked(address indexed beneficiary, uint256 amount); /** * @dev Only allow calls from the beneficiary of the contract */ modifier onlyBeneficiary() { require(msg.sender == beneficiary, "!auth"); _; } /** * @notice Initializes the contract * @param _owner Address of the contract owner * @param _beneficiary Address of the beneficiary of locked tokens * @param _managedAmount Amount of tokens to be managed by the lock contract * @param _startTime Start time of the release schedule * @param _endTime End time of the release schedule * @param _periods Number of periods between start time and end time * @param _releaseStartTime Override time for when the releases start * @param _vestingCliffTime Override time for when the vesting start * @param _revocable Whether the contract is revocable */ function _initialize( address _owner, address _beneficiary, address _token, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, Revocability _revocable ) internal { require(!isInitialized, "Already initialized"); require(_owner != address(0), "Owner cannot be zero"); require(_beneficiary != address(0), "Beneficiary cannot be zero"); require(_token != address(0), "Token cannot be zero"); require(_managedAmount > 0, "Managed tokens cannot be zero"); require(_startTime != 0, "Start time must be set"); require(_startTime < _endTime, "Start time > end time"); require(_periods >= MIN_PERIOD, "Periods cannot be below minimum"); require(_revocable != Revocability.NotSet, "Must set a revocability option"); require(_releaseStartTime < _endTime, "Release start time must be before end time"); require(_vestingCliffTime < _endTime, "Cliff time must be before end time"); isInitialized = true; Ownable.initialize(_owner); beneficiary = _beneficiary; token = IERC20(_token); managedAmount = _managedAmount; startTime = _startTime; endTime = _endTime; periods = _periods; // Optionals releaseStartTime = _releaseStartTime; vestingCliffTime = _vestingCliffTime; revocable = _revocable; } // -- Balances -- /** * @notice Returns the amount of tokens currently held by the contract * @return Tokens held in the contract */ function currentBalance() public override view returns (uint256) { return token.balanceOf(address(this)); } // -- Time & Periods -- /** * @notice Returns the current block timestamp * @return Current block timestamp */ function currentTime() public override view returns (uint256) { return block.timestamp; } /** * @notice Gets duration of contract from start to end in seconds * @return Amount of seconds from contract startTime to endTime */ function duration() public override view returns (uint256) { return endTime.sub(startTime); } /** * @notice Gets time elapsed since the start of the contract * @dev Returns zero if called before conctract starTime * @return Seconds elapsed from contract startTime */ function sinceStartTime() public override view returns (uint256) { uint256 current = currentTime(); if (current <= startTime) { return 0; } return current.sub(startTime); } /** * @notice Returns amount available to be released after each period according to schedule * @return Amount of tokens available after each period */ function amountPerPeriod() public override view returns (uint256) { return managedAmount.div(periods); } /** * @notice Returns the duration of each period in seconds * @return Duration of each period in seconds */ function periodDuration() public override view returns (uint256) { return duration().div(periods); } /** * @notice Gets the current period based on the schedule * @return A number that represents the current period */ function currentPeriod() public override view returns (uint256) { return sinceStartTime().div(periodDuration()).add(MIN_PERIOD); } /** * @notice Gets the number of periods that passed since the first period * @return A number of periods that passed since the schedule started */ function passedPeriods() public override view returns (uint256) { return currentPeriod().sub(MIN_PERIOD); } // -- Locking & Release Schedule -- /** * @notice Gets the currently available token according to the schedule * @dev Implements the step-by-step schedule based on periods for available tokens * @return Amount of tokens available according to the schedule */ function availableAmount() public override view returns (uint256) { uint256 current = currentTime(); // Before contract start no funds are available if (current < startTime) { return 0; } // After contract ended all funds are available if (current > endTime) { return managedAmount; } // Get available amount based on period return passedPeriods().mul(amountPerPeriod()); } /** * @notice Gets the amount of currently vested tokens * @dev Similar to available amount, but is fully vested when contract is non-revocable * @return Amount of tokens already vested */ function vestedAmount() public override view returns (uint256) { // If non-revocable it is fully vested if (revocable == Revocability.Disabled) { return managedAmount; } // Vesting cliff is activated and it has not passed means nothing is vested yet if (vestingCliffTime > 0 && currentTime() < vestingCliffTime) { return 0; } return availableAmount(); } /** * @notice Gets tokens currently available for release * @dev Considers the schedule and takes into account already released tokens * @return Amount of tokens ready to be released */ function releasableAmount() public override view returns (uint256) { // If a release start time is set no tokens are available for release before this date // If not set it follows the default schedule and tokens are available on // the first period passed if (releaseStartTime > 0 && currentTime() < releaseStartTime) { return 0; } // Vesting cliff is activated and it has not passed means nothing is vested yet // so funds cannot be released if (revocable == Revocability.Enabled && vestingCliffTime > 0 && currentTime() < vestingCliffTime) { return 0; } // A beneficiary can never have more releasable tokens than the contract balance uint256 releasable = availableAmount().sub(releasedAmount); return MathUtils.min(currentBalance(), releasable); } /** * @notice Gets the outstanding amount yet to be released based on the whole contract lifetime * @dev Does not consider schedule but just global amounts tracked * @return Amount of outstanding tokens for the lifetime of the contract */ function totalOutstandingAmount() public override view returns (uint256) { return managedAmount.sub(releasedAmount); } /** * @notice Gets surplus amount in the contract based on outstanding amount to release * @dev All funds over outstanding amount is considered surplus that can be withdrawn by beneficiary * @return Amount of tokens considered as surplus */ function surplusAmount() public override view returns (uint256) { uint256 balance = currentBalance(); uint256 outstandingAmount = totalOutstandingAmount(); if (balance > outstandingAmount) { return balance.sub(outstandingAmount); } return 0; } // -- Value Transfer -- /** * @notice Releases tokens based on the configured schedule * @dev All available releasable tokens are transferred to beneficiary */ function release() external override onlyBeneficiary { uint256 amountToRelease = releasableAmount(); require(amountToRelease > 0, "No available releasable amount"); releasedAmount = releasedAmount.add(amountToRelease); token.safeTransfer(beneficiary, amountToRelease); emit TokensReleased(beneficiary, amountToRelease); } /** * @notice Withdraws surplus, unmanaged tokens from the contract * @dev Tokens in the contract over outstanding amount are considered as surplus * @param _amount Amount of tokens to withdraw */ function withdrawSurplus(uint256 _amount) external override onlyBeneficiary { require(_amount > 0, "Amount cannot be zero"); require(surplusAmount() >= _amount, "Amount requested > surplus available"); token.safeTransfer(beneficiary, _amount); emit TokensWithdrawn(beneficiary, _amount); } /** * @notice Revokes a vesting schedule and return the unvested tokens to the owner * @dev Vesting schedule is always calculated based on managed tokens */ function revoke() external override onlyOwner { require(revocable == Revocability.Enabled, "Contract is non-revocable"); require(isRevoked == false, "Already revoked"); uint256 unvestedAmount = managedAmount.sub(vestedAmount()); require(unvestedAmount > 0, "No available unvested amount"); isRevoked = true; token.safeTransfer(owner(), unvestedAmount); emit TokensRevoked(beneficiary, unvestedAmount); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./IGraphTokenLock.sol"; interface IGraphTokenLockManager { // -- Factory -- function setMasterCopy(address _masterCopy) external; function createTokenLockWallet( address _owner, address _beneficiary, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, IGraphTokenLock.Revocability _revocable ) external; // -- Funds Management -- function token() external returns (IERC20); function deposit(uint256 _amount) external; function withdraw(uint256 _amount) external; // -- Allowed Funds Destinations -- function addTokenDestination(address _dst) external; function removeTokenDestination(address _dst) external; function isTokenDestination(address _dst) external view returns (bool); function getTokenDestinations() external view returns (address[] memory); // -- Function Call Authorization -- function setAuthFunctionCall(string calldata _signature, address _target) external; function unsetAuthFunctionCall(string calldata _signature) external; function setAuthFunctionCallMany(string[] calldata _signatures, address[] calldata _targets) external; function getAuthFunctionCallTarget(bytes4 _sigHash) external view returns (address); function isAuthFunctionCall(bytes4 _sigHash) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The owner account will be passed on initialization of the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function initialize(address owner) internal { _owner = owner; emit OwnershipTransferred(address(0), owner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == msg.sender, "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() external virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) external virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; library MathUtils { function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IGraphTokenLock { enum Revocability { NotSet, Enabled, Disabled } // -- Balances -- function currentBalance() external view returns (uint256); // -- Time & Periods -- function currentTime() external view returns (uint256); function duration() external view returns (uint256); function sinceStartTime() external view returns (uint256); function amountPerPeriod() external view returns (uint256); function periodDuration() external view returns (uint256); function currentPeriod() external view returns (uint256); function passedPeriods() external view returns (uint256); // -- Locking & Release Schedule -- function availableAmount() external view returns (uint256); function vestedAmount() external view returns (uint256); function releasableAmount() external view returns (uint256); function totalOutstandingAmount() external view returns (uint256); function surplusAmount() external view returns (uint256); // -- Value Transfer -- function release() external; function withdrawSurplus(uint256 _amount) external; function revoke() external; }
File 2 of 9: GraphProxy
// SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/utils/Address.sol"; import "./GraphProxyStorage.sol"; /** * @title Graph Proxy * @dev Graph Proxy contract used to delegate call implementation contracts and support upgrades. * This contract should NOT define storage as it is managed by GraphProxyStorage. * This contract implements a proxy that is upgradeable by an admin. * https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#transparent-proxies-and-function-clashes */ contract GraphProxy is GraphProxyStorage { /** * @dev Modifier used internally that will delegate the call to the implementation unless * the sender is the admin. */ modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } /** * @dev Modifier used internally that will delegate the call to the implementation unless * the sender is the admin or pending implementation. */ modifier ifAdminOrPendingImpl() { if (msg.sender == _admin() || msg.sender == _pendingImplementation()) { _; } else { _fallback(); } } /** * @dev Contract constructor. * @param _impl Address of the initial implementation * @param _admin Address of the proxy admin */ constructor(address _impl, address _admin) { assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); assert( IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1) ); assert( PENDING_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.pendingImplementation")) - 1) ); _setAdmin(_admin); _setPendingImplementation(_impl); } /** * @dev Returns the current admin. * * NOTE: Only the admin and implementation can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function admin() external ifAdminOrPendingImpl returns (address) { return _admin(); } /** * @dev Returns the current implementation. * * NOTE: Only the admin can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` */ function implementation() external ifAdminOrPendingImpl returns (address) { return _implementation(); } /** * @dev Returns the current pending implementation. * * NOTE: Only the admin can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x9e5eddc59e0b171f57125ab86bee043d9128098c3a6b9adb4f2e86333c2f6f8c` */ function pendingImplementation() external ifAdminOrPendingImpl returns (address) { return _pendingImplementation(); } /** * @dev Changes the admin of the proxy. * * NOTE: Only the admin can call this function. */ function setAdmin(address _newAdmin) external ifAdmin { require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); _setAdmin(_newAdmin); } /** * @dev Upgrades to a new implementation contract. * @param _newImplementation Address of implementation contract * * NOTE: Only the admin can call this function. */ function upgradeTo(address _newImplementation) external ifAdmin { _setPendingImplementation(_newImplementation); } /** * @dev Admin function for new implementation to accept its role as implementation. */ function acceptUpgrade() external ifAdminOrPendingImpl { _acceptUpgrade(); } /** * @dev Admin function for new implementation to accept its role as implementation. */ function acceptUpgradeAndCall(bytes calldata data) external ifAdminOrPendingImpl { _acceptUpgrade(); // solhint-disable-next-line avoid-low-level-calls (bool success, ) = _implementation().delegatecall(data); require(success); } /** * @dev Admin function for new implementation to accept its role as implementation. */ function _acceptUpgrade() internal { address _pendingImplementation = _pendingImplementation(); require(Address.isContract(_pendingImplementation), "Implementation must be a contract"); require( _pendingImplementation != address(0) && msg.sender == _pendingImplementation, "Caller must be the pending implementation" ); _setImplementation(_pendingImplementation); _setPendingImplementation(address(0)); } /** * @dev Delegates the current call to implementation. * This function does not return to its internal call site, it will return directly to the * external caller. */ function _fallback() internal { require(msg.sender != _admin(), "Cannot fallback to proxy target"); assembly { // (a) get free memory pointer let ptr := mload(0x40) // (b) get address of the implementation let impl := and(sload(IMPLEMENTATION_SLOT), 0xffffffffffffffffffffffffffffffffffffffff) // (1) copy incoming call data calldatacopy(ptr, 0, calldatasize()) // (2) forward call to logic contract let result := delegatecall(gas(), impl, ptr, calldatasize(), 0, 0) let size := returndatasize() // (3) retrieve return data returndatacopy(ptr, 0, size) // (4) forward return data back to caller switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } /** * @dev Fallback function that delegates calls to implementation. Will run if no other * function in the contract matches the call data. */ fallback() external payable { _fallback(); } /** * @dev Fallback function that delegates calls to implementation. Will run if call data * is empty. */ receive() external payable { _fallback(); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; /** * @title Graph Proxy Storage * @dev Contract functions related to getting and setting proxy storage. * This contract does not actually define state variables managed by the compiler * but uses fixed slot locations. */ contract GraphProxyStorage { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Storage slot with the address of the pending implementation. * This is the keccak-256 hash of "eip1967.proxy.pendingImplementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant PENDING_IMPLEMENTATION_SLOT = 0x9e5eddc59e0b171f57125ab86bee043d9128098c3a6b9adb4f2e86333c2f6f8c; /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Emitted when pendingImplementation is changed. */ event PendingImplementationUpdated( address indexed oldPendingImplementation, address indexed newPendingImplementation ); /** * @dev Emitted when pendingImplementation is accepted, * which means contract implementation is updated. */ event ImplementationUpdated( address indexed oldImplementation, address indexed newImplementation ); /** * @dev Emitted when the admin account has changed. */ event AdminUpdated(address indexed oldAdmin, address indexed newAdmin); /** * @dev Modifier to check whether the `msg.sender` is the admin. */ modifier onlyAdmin() { require(msg.sender == _admin(), "Caller must be admin"); _; } /** * @return adm The admin slot. */ function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } /** * @dev Sets the address of the proxy admin. * @param _newAdmin Address of the new proxy admin */ function _setAdmin(address _newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, _newAdmin) } emit AdminUpdated(_admin(), _newAdmin); } /** * @dev Returns the current implementation. * @return impl Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Returns the current pending implementation. * @return impl Address of the current pending implementation */ function _pendingImplementation() internal view returns (address impl) { bytes32 slot = PENDING_IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Sets the implementation address of the proxy. * @param _newImplementation Address of the new implementation */ function _setImplementation(address _newImplementation) internal { address oldImplementation = _implementation(); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, _newImplementation) } emit ImplementationUpdated(oldImplementation, _newImplementation); } /** * @dev Sets the pending implementation address of the proxy. * @param _newImplementation Address of the new pending implementation */ function _setPendingImplementation(address _newImplementation) internal { address oldPendingImplementation = _pendingImplementation(); bytes32 slot = PENDING_IMPLEMENTATION_SLOT; assembly { sstore(slot, _newImplementation) } emit PendingImplementationUpdated(oldPendingImplementation, _newImplementation); } }
File 3 of 9: GraphToken
// Sources flattened with hardhat v2.0.2 https://hardhat.org // File @openzeppelin/contracts/GSN/[email protected] // SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^0.7.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File @openzeppelin/contracts/math/[email protected] pragma solidity ^0.7.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^0.7.0; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin guidelines: functions revert instead * of returning `false` on failure. This behavior is nonetheless conventional * and does not conflict with the expectations of ERC20 applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20 { using SafeMath for uint256; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; uint8 private _decimals; /** * @dev Sets the values for {name} and {symbol}, initializes {decimals} with * a default value of 18. * * To select a different value for {decimals}, use {_setupDecimals}. * * All three of these values are immutable: they can only be set once during * construction. */ constructor (string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; _decimals = 18; } /** * @dev Returns the name of the token. */ function name() public view returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5,05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is * called. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view returns (uint8) { return _decimals; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `recipient` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address recipient, uint256 amount) public virtual override returns (bool) { _transfer(_msgSender(), recipient, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { _approve(_msgSender(), spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * Requirements: * * - `sender` and `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. * - the caller must have allowance for ``sender``'s tokens of at least * `amount`. */ function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) { _transfer(sender, recipient, amount); _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance")); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")); return true; } /** * @dev Moves tokens `amount` from `sender` to `recipient`. * * This is internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `sender` cannot be the zero address. * - `recipient` cannot be the zero address. * - `sender` must have a balance of at least `amount`. */ function _transfer(address sender, address recipient, uint256 amount) internal virtual { require(sender != address(0), "ERC20: transfer from the zero address"); require(recipient != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(sender, recipient, amount); _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); _balances[recipient] = _balances[recipient].add(amount); emit Transfer(sender, recipient, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `to` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply = _totalSupply.add(amount); _balances[account] = _balances[account].add(amount); emit Transfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); _totalSupply = _totalSupply.sub(amount); emit Transfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Sets {decimals} to a value other than the default one of 18. * * WARNING: This function should only be called from the constructor. Most * applications that interact with token contracts will not expect * {decimals} to ever change, and may work incorrectly if it does. */ function _setupDecimals(uint8 decimals_) internal { _decimals = decimals_; } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be to transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { } } // File @openzeppelin/contracts/token/ERC20/[email protected] pragma solidity ^0.7.0; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { using SafeMath for uint256; /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { uint256 decreasedAllowance = allowance(account, _msgSender()).sub(amount, "ERC20: burn amount exceeds allowance"); _approve(account, _msgSender(), decreasedAllowance); _burn(account, amount); } } // File @openzeppelin/contracts/cryptography/[email protected] pragma solidity ^0.7.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Check the signature length if (signature.length != 65) { revert("ECDSA: invalid signature length"); } // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value"); require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); require(signer != address(0), "ECDSA: invalid signature"); return signer; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } } // File contracts/governance/Governed.sol pragma solidity ^0.7.3; /** * @title Graph Governance contract * @dev All contracts that will be owned by a Governor entity should extend this contract. */ contract Governed { // -- State -- address public governor; address public pendingGovernor; // -- Events -- event NewPendingOwnership(address indexed from, address indexed to); event NewOwnership(address indexed from, address indexed to); /** * @dev Check if the caller is the governor. */ modifier onlyGovernor { require(msg.sender == governor, "Only Governor can call"); _; } /** * @dev Initialize the governor to the contract caller. */ function _initialize(address _initGovernor) internal { governor = _initGovernor; } /** * @dev Admin function to begin change of governor. The `_newGovernor` must call * `acceptOwnership` to finalize the transfer. * @param _newGovernor Address of new `governor` */ function transferOwnership(address _newGovernor) external onlyGovernor { require(_newGovernor != address(0), "Governor must be set"); address oldPendingGovernor = pendingGovernor; pendingGovernor = _newGovernor; emit NewPendingOwnership(oldPendingGovernor, pendingGovernor); } /** * @dev Admin function for pending governor to accept role and update governor. * This function must called by the pending governor. */ function acceptOwnership() external { require( pendingGovernor != address(0) && msg.sender == pendingGovernor, "Caller must be pending governor" ); address oldGovernor = governor; address oldPendingGovernor = pendingGovernor; governor = pendingGovernor; pendingGovernor = address(0); emit NewOwnership(oldGovernor, governor); emit NewPendingOwnership(oldPendingGovernor, pendingGovernor); } } // File contracts/token/GraphToken.sol pragma solidity ^0.7.3; /** * @title GraphToken contract * @dev This is the implementation of the ERC20 Graph Token. * The implementation exposes a Permit() function to allow for a spender to send a signed message * and approve funds to a spender following EIP2612 to make integration with other contracts easier. * * The token is initially owned by the deployer address that can mint tokens to create the initial * distribution. For convenience, an initial supply can be passed in the constructor that will be * assigned to the deployer. * * The governor can add the RewardsManager contract to mint indexing rewards. * */ contract GraphToken is Governed, ERC20, ERC20Burnable { using SafeMath for uint256; // -- EIP712 -- // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-712.md#definition-of-domainseparator bytes32 private constant DOMAIN_TYPE_HASH = keccak256( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" ); bytes32 private constant DOMAIN_NAME_HASH = keccak256("Graph Token"); bytes32 private constant DOMAIN_VERSION_HASH = keccak256("0"); bytes32 private constant DOMAIN_SALT = 0x51f3d585afe6dfeb2af01bba0889a36c1db03beec88c6a4d0c53817069026afa; // Randomly generated salt bytes32 private constant PERMIT_TYPEHASH = keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ); // -- State -- bytes32 private DOMAIN_SEPARATOR; mapping(address => bool) private _minters; mapping(address => uint256) public nonces; // -- Events -- event MinterAdded(address indexed account); event MinterRemoved(address indexed account); modifier onlyMinter() { require(isMinter(msg.sender), "Only minter can call"); _; } /** * @dev Graph Token Contract Constructor. * @param _initialSupply Initial supply of GRT */ constructor(uint256 _initialSupply) ERC20("Graph Token", "GRT") { Governed._initialize(msg.sender); // The Governor has the initial supply of tokens _mint(msg.sender, _initialSupply); // The Governor is the default minter _addMinter(msg.sender); // EIP-712 domain separator DOMAIN_SEPARATOR = keccak256( abi.encode( DOMAIN_TYPE_HASH, DOMAIN_NAME_HASH, DOMAIN_VERSION_HASH, _getChainID(), address(this), DOMAIN_SALT ) ); } /** * @dev Approve token allowance by validating a message signed by the holder. * @param _owner Address of the token holder * @param _spender Address of the approved spender * @param _value Amount of tokens to approve the spender * @param _deadline Expiration time of the signed permit * @param _v Signature version * @param _r Signature r value * @param _s Signature s value */ function permit( address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s ) external { bytes32 digest = keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR, keccak256( abi.encode( PERMIT_TYPEHASH, _owner, _spender, _value, nonces[_owner], _deadline ) ) ) ); nonces[_owner] = nonces[_owner].add(1); address recoveredAddress = ECDSA.recover(digest, abi.encodePacked(_r, _s, _v)); require(_owner == recoveredAddress, "GRT: invalid permit"); require(_deadline == 0 || block.timestamp <= _deadline, "GRT: expired permit"); _approve(_owner, _spender, _value); } /** * @dev Add a new minter. * @param _account Address of the minter */ function addMinter(address _account) external onlyGovernor { _addMinter(_account); } /** * @dev Remove a minter. * @param _account Address of the minter */ function removeMinter(address _account) external onlyGovernor { _removeMinter(_account); } /** * @dev Renounce to be a minter. */ function renounceMinter() external { _removeMinter(msg.sender); } /** * @dev Mint new tokens. * @param _to Address to send the newly minted tokens * @param _amount Amount of tokens to mint */ function mint(address _to, uint256 _amount) external onlyMinter { _mint(_to, _amount); } /** * @dev Return if the `_account` is a minter or not. * @param _account Address to check * @return True if the `_account` is minter */ function isMinter(address _account) public view returns (bool) { return _minters[_account]; } /** * @dev Add a new minter. * @param _account Address of the minter */ function _addMinter(address _account) private { _minters[_account] = true; emit MinterAdded(_account); } /** * @dev Remove a minter. * @param _account Address of the minter */ function _removeMinter(address _account) private { _minters[_account] = false; emit MinterRemoved(_account); } /** * @dev Get the running network chain ID. * @return The chain ID */ function _getChainID() private pure returns (uint256) { uint256 id; assembly { id := chainid() } return id; } }
File 4 of 9: GraphTokenLockWallet
// SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./GraphTokenLock.sol"; import "./IGraphTokenLockManager.sol"; /** * @title GraphTokenLockWallet * @notice This contract is built on top of the base GraphTokenLock functionality. * It allows wallet beneficiaries to use the deposited funds to perform specific function calls * on specific contracts. * * The idea is that supporters with locked tokens can participate in the protocol * but disallow any release before the vesting/lock schedule. * The beneficiary can issue authorized function calls to this contract that will * get forwarded to a target contract. A target contract is any of our protocol contracts. * The function calls allowed are queried to the GraphTokenLockManager, this way * the same configuration can be shared for all the created lock wallet contracts. * * NOTE: Contracts used as target must have its function signatures checked to avoid collisions * with any of this contract functions. * Beneficiaries need to approve the use of the tokens to the protocol contracts. For convenience * the maximum amount of tokens is authorized. */ contract GraphTokenLockWallet is GraphTokenLock { using SafeMath for uint256; using SafeERC20 for IERC20; // -- State -- IGraphTokenLockManager public manager; uint256 public usedAmount; uint256 private constant MAX_UINT256 = 2**256 - 1; // -- Events -- event ManagerUpdated(address indexed _oldManager, address indexed _newManager); event TokenDestinationsApproved(); event TokenDestinationsRevoked(); // Initializer function initialize( address _manager, address _owner, address _beneficiary, address _token, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, Revocability _revocable ) external { _initialize( _owner, _beneficiary, _token, _managedAmount, _startTime, _endTime, _periods, _releaseStartTime, _vestingCliffTime, _revocable ); _setManager(_manager); } // -- Admin -- /** * @notice Sets a new manager for this contract * @param _newManager Address of the new manager */ function setManager(address _newManager) external onlyOwner { _setManager(_newManager); } /** * @dev Sets a new manager for this contract * @param _newManager Address of the new manager */ function _setManager(address _newManager) private { require(_newManager != address(0), "Manager cannot be empty"); require(Address.isContract(_newManager), "Manager must be a contract"); address oldManager = address(manager); manager = IGraphTokenLockManager(_newManager); emit ManagerUpdated(oldManager, _newManager); } // -- Beneficiary -- /** * @notice Approves protocol access of the tokens managed by this contract * @dev Approves all token destinations registered in the manager to pull tokens */ function approveProtocol() external onlyBeneficiary { address[] memory dstList = manager.getTokenDestinations(); for (uint256 i = 0; i < dstList.length; i++) { token.safeApprove(dstList[i], MAX_UINT256); } emit TokenDestinationsApproved(); } /** * @notice Revokes protocol access of the tokens managed by this contract * @dev Revokes approval to all token destinations in the manager to pull tokens */ function revokeProtocol() external onlyBeneficiary { address[] memory dstList = manager.getTokenDestinations(); for (uint256 i = 0; i < dstList.length; i++) { token.safeApprove(dstList[i], 0); } emit TokenDestinationsRevoked(); } /** * @notice Forward authorized contract calls to protocol contracts * @dev Fallback function can be called by the beneficiary only if function call is allowed */ fallback() external payable { // Only beneficiary can forward calls require(msg.sender == beneficiary, "Unauthorized caller"); // Function call validation address _target = manager.getAuthFunctionCallTarget(msg.sig); require(_target != address(0), "Unauthorized function"); uint256 oldBalance = currentBalance(); // Call function with data Address.functionCall(_target, msg.data); // Tracked used tokens in the protocol // We do this check after balances were updated by the forwarded call // Check is only enforced for revocable contracts to save some gas if (revocable == Revocability.Enabled) { // Track contract balance change uint256 newBalance = currentBalance(); if (newBalance < oldBalance) { // Outflow uint256 diff = oldBalance.sub(newBalance); usedAmount = usedAmount.add(diff); } else { // Inflow: We can receive profits from the protocol, that could make usedAmount to // underflow. We set it to zero in that case. uint256 diff = newBalance.sub(oldBalance); usedAmount = (diff >= usedAmount) ? 0 : usedAmount.sub(diff); } require(usedAmount <= vestedAmount(), "Cannot use more tokens than vested amount"); } } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "./Ownable.sol"; import "./MathUtils.sol"; import "./IGraphTokenLock.sol"; /** * @title GraphTokenLock * @notice Contract that manages an unlocking schedule of tokens. * @dev The contract lock manage a number of tokens deposited into the contract to ensure that * they can only be released under certain time conditions. * * This contract implements a release scheduled based on periods and tokens are released in steps * after each period ends. It can be configured with one period in which case it is like a plain TimeLock. * It also supports revocation to be used for vesting schedules. * * The contract supports receiving extra funds than the managed tokens ones that can be * withdrawn by the beneficiary at any time. * * A releaseStartTime parameter is included to override the default release schedule and * perform the first release on the configured time. After that it will continue with the * default schedule. */ abstract contract GraphTokenLock is Ownable, IGraphTokenLock { using SafeMath for uint256; using SafeERC20 for IERC20; uint256 private constant MIN_PERIOD = 1; // -- State -- IERC20 public token; address public beneficiary; // Configuration // Amount of tokens managed by the contract schedule uint256 public managedAmount; uint256 public startTime; // Start datetime (in unixtimestamp) uint256 public endTime; // Datetime after all funds are fully vested/unlocked (in unixtimestamp) uint256 public periods; // Number of vesting/release periods // First release date for tokens (in unixtimestamp) // If set, no tokens will be released before releaseStartTime ignoring // the amount to release each period uint256 public releaseStartTime; // A cliff set a date to which a beneficiary needs to get to vest // all preceding periods uint256 public vestingCliffTime; Revocability public revocable; // Whether to use vesting for locked funds // State bool public isRevoked; bool public isInitialized; uint256 public releasedAmount; // -- Events -- event TokensReleased(address indexed beneficiary, uint256 amount); event TokensWithdrawn(address indexed beneficiary, uint256 amount); event TokensRevoked(address indexed beneficiary, uint256 amount); /** * @dev Only allow calls from the beneficiary of the contract */ modifier onlyBeneficiary() { require(msg.sender == beneficiary, "!auth"); _; } /** * @notice Initializes the contract * @param _owner Address of the contract owner * @param _beneficiary Address of the beneficiary of locked tokens * @param _managedAmount Amount of tokens to be managed by the lock contract * @param _startTime Start time of the release schedule * @param _endTime End time of the release schedule * @param _periods Number of periods between start time and end time * @param _releaseStartTime Override time for when the releases start * @param _vestingCliffTime Override time for when the vesting start * @param _revocable Whether the contract is revocable */ function _initialize( address _owner, address _beneficiary, address _token, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, Revocability _revocable ) internal { require(!isInitialized, "Already initialized"); require(_owner != address(0), "Owner cannot be zero"); require(_beneficiary != address(0), "Beneficiary cannot be zero"); require(_token != address(0), "Token cannot be zero"); require(_managedAmount > 0, "Managed tokens cannot be zero"); require(_startTime != 0, "Start time must be set"); require(_startTime < _endTime, "Start time > end time"); require(_periods >= MIN_PERIOD, "Periods cannot be below minimum"); require(_revocable != Revocability.NotSet, "Must set a revocability option"); require(_releaseStartTime < _endTime, "Release start time must be before end time"); require(_vestingCliffTime < _endTime, "Cliff time must be before end time"); isInitialized = true; Ownable.initialize(_owner); beneficiary = _beneficiary; token = IERC20(_token); managedAmount = _managedAmount; startTime = _startTime; endTime = _endTime; periods = _periods; // Optionals releaseStartTime = _releaseStartTime; vestingCliffTime = _vestingCliffTime; revocable = _revocable; } // -- Balances -- /** * @notice Returns the amount of tokens currently held by the contract * @return Tokens held in the contract */ function currentBalance() public override view returns (uint256) { return token.balanceOf(address(this)); } // -- Time & Periods -- /** * @notice Returns the current block timestamp * @return Current block timestamp */ function currentTime() public override view returns (uint256) { return block.timestamp; } /** * @notice Gets duration of contract from start to end in seconds * @return Amount of seconds from contract startTime to endTime */ function duration() public override view returns (uint256) { return endTime.sub(startTime); } /** * @notice Gets time elapsed since the start of the contract * @dev Returns zero if called before conctract starTime * @return Seconds elapsed from contract startTime */ function sinceStartTime() public override view returns (uint256) { uint256 current = currentTime(); if (current <= startTime) { return 0; } return current.sub(startTime); } /** * @notice Returns amount available to be released after each period according to schedule * @return Amount of tokens available after each period */ function amountPerPeriod() public override view returns (uint256) { return managedAmount.div(periods); } /** * @notice Returns the duration of each period in seconds * @return Duration of each period in seconds */ function periodDuration() public override view returns (uint256) { return duration().div(periods); } /** * @notice Gets the current period based on the schedule * @return A number that represents the current period */ function currentPeriod() public override view returns (uint256) { return sinceStartTime().div(periodDuration()).add(MIN_PERIOD); } /** * @notice Gets the number of periods that passed since the first period * @return A number of periods that passed since the schedule started */ function passedPeriods() public override view returns (uint256) { return currentPeriod().sub(MIN_PERIOD); } // -- Locking & Release Schedule -- /** * @notice Gets the currently available token according to the schedule * @dev Implements the step-by-step schedule based on periods for available tokens * @return Amount of tokens available according to the schedule */ function availableAmount() public override view returns (uint256) { uint256 current = currentTime(); // Before contract start no funds are available if (current < startTime) { return 0; } // After contract ended all funds are available if (current > endTime) { return managedAmount; } // Get available amount based on period return passedPeriods().mul(amountPerPeriod()); } /** * @notice Gets the amount of currently vested tokens * @dev Similar to available amount, but is fully vested when contract is non-revocable * @return Amount of tokens already vested */ function vestedAmount() public override view returns (uint256) { // If non-revocable it is fully vested if (revocable == Revocability.Disabled) { return managedAmount; } // Vesting cliff is activated and it has not passed means nothing is vested yet if (vestingCliffTime > 0 && currentTime() < vestingCliffTime) { return 0; } return availableAmount(); } /** * @notice Gets tokens currently available for release * @dev Considers the schedule and takes into account already released tokens * @return Amount of tokens ready to be released */ function releasableAmount() public override view returns (uint256) { // If a release start time is set no tokens are available for release before this date // If not set it follows the default schedule and tokens are available on // the first period passed if (releaseStartTime > 0 && currentTime() < releaseStartTime) { return 0; } // Vesting cliff is activated and it has not passed means nothing is vested yet // so funds cannot be released if (revocable == Revocability.Enabled && vestingCliffTime > 0 && currentTime() < vestingCliffTime) { return 0; } // A beneficiary can never have more releasable tokens than the contract balance uint256 releasable = availableAmount().sub(releasedAmount); return MathUtils.min(currentBalance(), releasable); } /** * @notice Gets the outstanding amount yet to be released based on the whole contract lifetime * @dev Does not consider schedule but just global amounts tracked * @return Amount of outstanding tokens for the lifetime of the contract */ function totalOutstandingAmount() public override view returns (uint256) { return managedAmount.sub(releasedAmount); } /** * @notice Gets surplus amount in the contract based on outstanding amount to release * @dev All funds over outstanding amount is considered surplus that can be withdrawn by beneficiary * @return Amount of tokens considered as surplus */ function surplusAmount() public override view returns (uint256) { uint256 balance = currentBalance(); uint256 outstandingAmount = totalOutstandingAmount(); if (balance > outstandingAmount) { return balance.sub(outstandingAmount); } return 0; } // -- Value Transfer -- /** * @notice Releases tokens based on the configured schedule * @dev All available releasable tokens are transferred to beneficiary */ function release() external override onlyBeneficiary { uint256 amountToRelease = releasableAmount(); require(amountToRelease > 0, "No available releasable amount"); releasedAmount = releasedAmount.add(amountToRelease); token.safeTransfer(beneficiary, amountToRelease); emit TokensReleased(beneficiary, amountToRelease); } /** * @notice Withdraws surplus, unmanaged tokens from the contract * @dev Tokens in the contract over outstanding amount are considered as surplus * @param _amount Amount of tokens to withdraw */ function withdrawSurplus(uint256 _amount) external override onlyBeneficiary { require(_amount > 0, "Amount cannot be zero"); require(surplusAmount() >= _amount, "Amount requested > surplus available"); token.safeTransfer(beneficiary, _amount); emit TokensWithdrawn(beneficiary, _amount); } /** * @notice Revokes a vesting schedule and return the unvested tokens to the owner * @dev Vesting schedule is always calculated based on managed tokens */ function revoke() external override onlyOwner { require(revocable == Revocability.Enabled, "Contract is non-revocable"); require(isRevoked == false, "Already revoked"); uint256 unvestedAmount = managedAmount.sub(vestedAmount()); require(unvestedAmount > 0, "No available unvested amount"); isRevoked = true; token.safeTransfer(owner(), unvestedAmount); emit TokensRevoked(beneficiary, unvestedAmount); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./IGraphTokenLock.sol"; interface IGraphTokenLockManager { // -- Factory -- function setMasterCopy(address _masterCopy) external; function createTokenLockWallet( address _owner, address _beneficiary, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, IGraphTokenLock.Revocability _revocable ) external; // -- Funds Management -- function token() external returns (IERC20); function deposit(uint256 _amount) external; function withdraw(uint256 _amount) external; // -- Allowed Funds Destinations -- function addTokenDestination(address _dst) external; function removeTokenDestination(address _dst) external; function isTokenDestination(address _dst) external view returns (bool); function getTokenDestinations() external view returns (address[] memory); // -- Function Call Authorization -- function setAuthFunctionCall(string calldata _signature, address _target) external; function unsetAuthFunctionCall(string calldata _signature) external; function setAuthFunctionCallMany(string[] calldata _signatures, address[] calldata _targets) external; function getAuthFunctionCallTarget(bytes4 _sigHash) external view returns (address); function isAuthFunctionCall(bytes4 _sigHash) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The owner account will be passed on initialization of the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ contract Ownable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function initialize(address owner) internal { _owner = owner; emit OwnershipTransferred(address(0), owner); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == msg.sender, "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() external virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) external virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; library MathUtils { function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IGraphTokenLock { enum Revocability { NotSet, Enabled, Disabled } // -- Balances -- function currentBalance() external view returns (uint256); // -- Time & Periods -- function currentTime() external view returns (uint256); function duration() external view returns (uint256); function sinceStartTime() external view returns (uint256); function amountPerPeriod() external view returns (uint256); function periodDuration() external view returns (uint256); function currentPeriod() external view returns (uint256); function passedPeriods() external view returns (uint256); // -- Locking & Release Schedule -- function availableAmount() external view returns (uint256); function vestedAmount() external view returns (uint256); function releasableAmount() external view returns (uint256); function totalOutstandingAmount() external view returns (uint256); function surplusAmount() external view returns (uint256); // -- Value Transfer -- function release() external; function withdrawSurplus(uint256 _amount) external; function revoke() external; }
File 5 of 9: GraphTokenLockManager
// SPDX-License-Identifier: MIT pragma solidity ^0.7.3; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/utils/EnumerableSet.sol"; import "./MinimalProxyFactory.sol"; import "./IGraphTokenLockManager.sol"; /** * @title GraphTokenLockManager * @notice This contract manages a list of authorized function calls and targets that can be called * by any TokenLockWallet contract and it is a factory of TokenLockWallet contracts. * * This contract receives funds to make the process of creating TokenLockWallet contracts * easier by distributing them the initial tokens to be managed. * * The owner can setup a list of token destinations that will be used by TokenLock contracts to * approve the pulling of funds, this way in can be guaranteed that only protocol contracts * will manipulate users funds. */ contract GraphTokenLockManager is MinimalProxyFactory, IGraphTokenLockManager { using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.AddressSet; // -- State -- mapping(bytes4 => address) public authFnCalls; EnumerableSet.AddressSet private _tokenDestinations; address public masterCopy; IERC20 private _token; // -- Events -- event MasterCopyUpdated(address indexed masterCopy); event TokenLockCreated( address indexed contractAddress, bytes32 indexed initHash, address indexed beneficiary, address token, uint256 managedAmount, uint256 startTime, uint256 endTime, uint256 periods, uint256 releaseStartTime, uint256 vestingCliffTime, IGraphTokenLock.Revocability revocable ); event TokensDeposited(address indexed sender, uint256 amount); event TokensWithdrawn(address indexed sender, uint256 amount); event FunctionCallAuth(address indexed caller, bytes4 indexed sigHash, address indexed target, string signature); event TokenDestinationAllowed(address indexed dst, bool allowed); /** * Constructor. * @param _graphToken Token to use for deposits and withdrawals * @param _masterCopy Address of the master copy to use to clone proxies */ constructor(IERC20 _graphToken, address _masterCopy) { require(address(_graphToken) != address(0), "Token cannot be zero"); _token = _graphToken; setMasterCopy(_masterCopy); } // -- Factory -- /** * @notice Sets the masterCopy bytecode to use to create clones of TokenLock contracts * @param _masterCopy Address of contract bytecode to factory clone */ function setMasterCopy(address _masterCopy) public override onlyOwner { require(_masterCopy != address(0), "MasterCopy cannot be zero"); masterCopy = _masterCopy; emit MasterCopyUpdated(_masterCopy); } /** * @notice Creates and fund a new token lock wallet using a minimum proxy * @param _owner Address of the contract owner * @param _beneficiary Address of the beneficiary of locked tokens * @param _managedAmount Amount of tokens to be managed by the lock contract * @param _startTime Start time of the release schedule * @param _endTime End time of the release schedule * @param _periods Number of periods between start time and end time * @param _releaseStartTime Override time for when the releases start * @param _revocable Whether the contract is revocable */ function createTokenLockWallet( address _owner, address _beneficiary, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, IGraphTokenLock.Revocability _revocable ) external override onlyOwner { require(_token.balanceOf(address(this)) >= _managedAmount, "Not enough tokens to create lock"); // Create contract using a minimal proxy and call initializer bytes memory initializer = abi.encodeWithSignature( "initialize(address,address,address,address,uint256,uint256,uint256,uint256,uint256,uint256,uint8)", address(this), _owner, _beneficiary, address(_token), _managedAmount, _startTime, _endTime, _periods, _releaseStartTime, _vestingCliffTime, _revocable ); address contractAddress = _deployProxy2(keccak256(initializer), masterCopy, initializer); // Send managed amount to the created contract _token.safeTransfer(contractAddress, _managedAmount); emit TokenLockCreated( contractAddress, keccak256(initializer), _beneficiary, address(_token), _managedAmount, _startTime, _endTime, _periods, _releaseStartTime, _vestingCliffTime, _revocable ); } // -- Funds Management -- /** * @notice Gets the GRT token address * @return Token used for transfers and approvals */ function token() external view override returns (IERC20) { return _token; } /** * @notice Deposits tokens into the contract * @dev Even if the ERC20 token can be transferred directly to the contract * this function provide a safe interface to do the transfer and avoid mistakes * @param _amount Amount to deposit */ function deposit(uint256 _amount) external override { require(_amount > 0, "Amount cannot be zero"); _token.safeTransferFrom(msg.sender, address(this), _amount); emit TokensDeposited(msg.sender, _amount); } /** * @notice Withdraws tokens from the contract * @dev Escape hatch in case of mistakes or to recover remaining funds * @param _amount Amount of tokens to withdraw */ function withdraw(uint256 _amount) external override onlyOwner { require(_amount > 0, "Amount cannot be zero"); _token.safeTransfer(msg.sender, _amount); emit TokensWithdrawn(msg.sender, _amount); } // -- Token Destinations -- /** * @notice Adds an address that can be allowed by a token lock to pull funds * @param _dst Destination address */ function addTokenDestination(address _dst) external override onlyOwner { require(_dst != address(0), "Destination cannot be zero"); require(_tokenDestinations.add(_dst), "Destination already added"); emit TokenDestinationAllowed(_dst, true); } /** * @notice Removes an address that can be allowed by a token lock to pull funds * @param _dst Destination address */ function removeTokenDestination(address _dst) external override onlyOwner { require(_tokenDestinations.remove(_dst), "Destination already removed"); emit TokenDestinationAllowed(_dst, false); } /** * @notice Returns True if the address is authorized to be a destination of tokens * @param _dst Destination address * @return True if authorized */ function isTokenDestination(address _dst) external view override returns (bool) { return _tokenDestinations.contains(_dst); } /** * @notice Returns an array of authorized destination addresses * @return Array of addresses authorized to pull funds from a token lock */ function getTokenDestinations() external view override returns (address[] memory) { address[] memory dstList = new address[](_tokenDestinations.length()); for (uint256 i = 0; i < _tokenDestinations.length(); i++) { dstList[i] = _tokenDestinations.at(i); } return dstList; } // -- Function Call Authorization -- /** * @notice Sets an authorized function call to target * @dev Input expected is the function signature as 'transfer(address,uint256)' * @param _signature Function signature * @param _target Address of the destination contract to call */ function setAuthFunctionCall(string calldata _signature, address _target) external override onlyOwner { _setAuthFunctionCall(_signature, _target); } /** * @notice Unsets an authorized function call to target * @dev Input expected is the function signature as 'transfer(address,uint256)' * @param _signature Function signature */ function unsetAuthFunctionCall(string calldata _signature) external override onlyOwner { bytes4 sigHash = _toFunctionSigHash(_signature); authFnCalls[sigHash] = address(0); emit FunctionCallAuth(msg.sender, sigHash, address(0), _signature); } /** * @notice Sets an authorized function call to target in bulk * @dev Input expected is the function signature as 'transfer(address,uint256)' * @param _signatures Function signatures * @param _targets Address of the destination contract to call */ function setAuthFunctionCallMany(string[] calldata _signatures, address[] calldata _targets) external override onlyOwner { require(_signatures.length == _targets.length, "Array length mismatch"); for (uint256 i = 0; i < _signatures.length; i++) { _setAuthFunctionCall(_signatures[i], _targets[i]); } } /** * @notice Sets an authorized function call to target * @dev Input expected is the function signature as 'transfer(address,uint256)' * @dev Function signatures of Graph Protocol contracts to be used are known ahead of time * @param _signature Function signature * @param _target Address of the destination contract to call */ function _setAuthFunctionCall(string calldata _signature, address _target) internal { require(_target != address(this), "Target must be other contract"); require(Address.isContract(_target), "Target must be a contract"); bytes4 sigHash = _toFunctionSigHash(_signature); authFnCalls[sigHash] = _target; emit FunctionCallAuth(msg.sender, sigHash, _target, _signature); } /** * @notice Gets the target contract to call for a particular function signature * @param _sigHash Function signature hash * @return Address of the target contract where to send the call */ function getAuthFunctionCallTarget(bytes4 _sigHash) public view override returns (address) { return authFnCalls[_sigHash]; } /** * @notice Returns true if the function call is authorized * @param _sigHash Function signature hash * @return True if authorized */ function isAuthFunctionCall(bytes4 _sigHash) external view override returns (bool) { return getAuthFunctionCallTarget(_sigHash) != address(0); } /** * @dev Converts a function signature string to 4-bytes hash * @param _signature Function signature string * @return Function signature hash */ function _toFunctionSigHash(string calldata _signature) internal pure returns (bytes4) { return _convertToBytes4(abi.encodeWithSignature(_signature)); } /** * @dev Converts function signature bytes to function signature hash (bytes4) * @param _signature Function signature * @return Function signature in bytes4 */ function _convertToBytes4(bytes memory _signature) internal pure returns (bytes4) { require(_signature.length == 4, "Invalid method signature"); bytes4 sigHash; // solium-disable-next-line security/no-inline-assembly assembly { sigHash := mload(add(_signature, 32)) } return sigHash; } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC20.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' // solhint-disable-next-line max-line-length require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping (bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. bytes32 lastvalue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastvalue; // Update the index for the moved value set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { require(set._values.length > index, "EnumerableSet: index out of bounds"); return set._values[index]; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(value))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(value))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(value))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint256(_at(set._inner, index))); } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values on the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Create2.sol"; // Adapted from https://github.com/OpenZeppelin/openzeppelin-sdk/blob/v2.5.0/packages/lib/contracts/upgradeability/ProxyFactory.sol // Based on https://eips.ethereum.org/EIPS/eip-1167 contract MinimalProxyFactory is Ownable { // -- Events -- event ProxyCreated(address indexed proxy); /** * @notice Gets the deterministic CREATE2 address for MinimalProxy with a particular implementation * @param _salt Bytes32 salt to use for CREATE2 * @param _implementation Address of the proxy target implementation * @return Address of the counterfactual MinimalProxy */ function getDeploymentAddress(bytes32 _salt, address _implementation) external view returns (address) { return Create2.computeAddress(_salt, keccak256(_getContractCreationCode(_implementation)), address(this)); } /** * @notice Deploys a MinimalProxy with CREATE2 * @param _salt Bytes32 salt to use for CREATE2 * @param _implementation Address of the proxy target implementation * @param _data Bytes with the initializer call * @return Address of the deployed MinimalProxy */ function _deployProxy2( bytes32 _salt, address _implementation, bytes memory _data ) internal returns (address) { address proxyAddress = Create2.deploy(0, _salt, _getContractCreationCode(_implementation)); emit ProxyCreated(proxyAddress); // Call function with data if (_data.length > 0) { Address.functionCall(proxyAddress, _data); } return proxyAddress; } /** * @notice Gets the MinimalProxy bytecode * @param _implementation Address of the proxy target implementation * @return MinimalProxy bytecode */ function _getContractCreationCode(address _implementation) internal pure returns (bytes memory) { bytes10 creation = 0x3d602d80600a3d3981f3; bytes10 prefix = 0x363d3d373d3d3d363d73; bytes20 targetBytes = bytes20(_implementation); bytes15 suffix = 0x5af43d82803e903d91602b57fd5bf3; return abi.encodePacked(creation, prefix, targetBytes, suffix); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "./IGraphTokenLock.sol"; interface IGraphTokenLockManager { // -- Factory -- function setMasterCopy(address _masterCopy) external; function createTokenLockWallet( address _owner, address _beneficiary, uint256 _managedAmount, uint256 _startTime, uint256 _endTime, uint256 _periods, uint256 _releaseStartTime, uint256 _vestingCliffTime, IGraphTokenLock.Revocability _revocable ) external; // -- Funds Management -- function token() external returns (IERC20); function deposit(uint256 _amount) external; function withdraw(uint256 _amount) external; // -- Allowed Funds Destinations -- function addTokenDestination(address _dst) external; function removeTokenDestination(address _dst) external; function isTokenDestination(address _dst) external view returns (bool); function getTokenDestinations() external view returns (address[] memory); // -- Function Call Authorization -- function setAuthFunctionCall(string calldata _signature, address _target) external; function unsetAuthFunctionCall(string calldata _signature) external; function setAuthFunctionCallMany(string[] calldata _signatures, address[] calldata _targets) external; function getAuthFunctionCallTarget(bytes4 _sigHash) external view returns (address); function isAuthFunctionCall(bytes4 _sigHash) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../GSN/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor () internal { address msgSender = _msgSender(); _owner = msgSender; emit OwnershipTransferred(address(0), msgSender); } /** * @dev Returns the address of the current owner. */ function owner() public view returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(_owner == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { emit OwnershipTransferred(_owner, address(0)); _owner = address(0); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); emit OwnershipTransferred(_owner, newOwner); _owner = newOwner; } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer. * `CREATE2` can be used to compute in advance the address where a smart * contract will be deployed, which allows for interesting new mechanisms known * as 'counterfactual interactions'. * * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more * information. */ library Create2 { /** * @dev Deploys a contract using `CREATE2`. The address where the contract * will be deployed can be known in advance via {computeAddress}. * * The bytecode for a contract can be obtained from Solidity with * `type(contractName).creationCode`. * * Requirements: * * - `bytecode` must not be empty. * - `salt` must have not been used for `bytecode` already. * - the factory must have a balance of at least `amount`. * - if `amount` is non-zero, `bytecode` must have a `payable` constructor. */ function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address) { address addr; require(address(this).balance >= amount, "Create2: insufficient balance"); require(bytecode.length != 0, "Create2: bytecode length is zero"); // solhint-disable-next-line no-inline-assembly assembly { addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt) } require(addr != address(0), "Create2: Failed on deploy"); return addr; } /** * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the * `bytecodeHash` or `salt` will result in a new destination address. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) { return computeAddress(salt, bytecodeHash, address(this)); } /** * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}. */ function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address) { bytes32 _data = keccak256( abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash) ); return address(uint256(_data)); } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /* * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with GSN meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IGraphTokenLock { enum Revocability { NotSet, Enabled, Disabled } // -- Balances -- function currentBalance() external view returns (uint256); // -- Time & Periods -- function currentTime() external view returns (uint256); function duration() external view returns (uint256); function sinceStartTime() external view returns (uint256); function amountPerPeriod() external view returns (uint256); function periodDuration() external view returns (uint256); function currentPeriod() external view returns (uint256); function passedPeriods() external view returns (uint256); // -- Locking & Release Schedule -- function availableAmount() external view returns (uint256); function vestedAmount() external view returns (uint256); function releasableAmount() external view returns (uint256); function totalOutstandingAmount() external view returns (uint256); function surplusAmount() external view returns (uint256); // -- Value Transfer -- function release() external; function withdrawSurplus(uint256 _amount) external; function revoke() external; }
File 6 of 9: Staking
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "@openzeppelin/contracts/cryptography/ECDSA.sol"; import "../base/Multicall.sol"; import "../upgrades/GraphUpgradeable.sol"; import "../utils/TokenUtils.sol"; import "./IStaking.sol"; import "./StakingStorage.sol"; import "./libs/MathUtils.sol"; import "./libs/Rebates.sol"; import "./libs/Stakes.sol"; /** * @title Staking contract * @dev The Staking contract allows Indexers to Stake on Subgraphs. Indexers Stake by creating * Allocations on a Subgraph. It also allows Delegators to Delegate towards an Indexer. The * contract also has the slashing functionality. */ contract Staking is StakingV2Storage, GraphUpgradeable, IStaking, Multicall { using SafeMath for uint256; using Stakes for Stakes.Indexer; using Rebates for Rebates.Pool; // 100% in parts per million uint32 private constant MAX_PPM = 1000000; // -- Events -- /** * @dev Emitted when `indexer` update the delegation parameters for its delegation pool. */ event DelegationParametersUpdated( address indexed indexer, uint32 indexingRewardCut, uint32 queryFeeCut, uint32 cooldownBlocks ); /** * @dev Emitted when `indexer` stake `tokens` amount. */ event StakeDeposited(address indexed indexer, uint256 tokens); /** * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block. */ event StakeLocked(address indexed indexer, uint256 tokens, uint256 until); /** * @dev Emitted when `indexer` withdrew `tokens` staked. */ event StakeWithdrawn(address indexed indexer, uint256 tokens); /** * @dev Emitted when `indexer` was slashed for a total of `tokens` amount. * Tracks `reward` amount of tokens given to `beneficiary`. */ event StakeSlashed( address indexed indexer, uint256 tokens, uint256 reward, address beneficiary ); /** * @dev Emitted when `delegator` delegated `tokens` to the `indexer`, the delegator * gets `shares` for the delegation pool proportionally to the tokens staked. */ event StakeDelegated( address indexed indexer, address indexed delegator, uint256 tokens, uint256 shares ); /** * @dev Emitted when `delegator` undelegated `tokens` from `indexer`. * Tokens get locked for withdrawal after a period of time. */ event StakeDelegatedLocked( address indexed indexer, address indexed delegator, uint256 tokens, uint256 shares, uint256 until ); /** * @dev Emitted when `delegator` withdrew delegated `tokens` from `indexer`. */ event StakeDelegatedWithdrawn( address indexed indexer, address indexed delegator, uint256 tokens ); /** * @dev Emitted when `indexer` allocated `tokens` amount to `subgraphDeploymentID` * during `epoch`. * `allocationID` indexer derived address used to identify the allocation. * `metadata` additional information related to the allocation. */ event AllocationCreated( address indexed indexer, bytes32 indexed subgraphDeploymentID, uint256 epoch, uint256 tokens, address indexed allocationID, bytes32 metadata ); /** * @dev Emitted when `indexer` collected `tokens` amount in `epoch` for `allocationID`. * These funds are related to `subgraphDeploymentID`. * The `from` value is the sender of the collected funds. */ event AllocationCollected( address indexed indexer, bytes32 indexed subgraphDeploymentID, uint256 epoch, uint256 tokens, address indexed allocationID, address from, uint256 curationFees, uint256 rebateFees ); /** * @dev Emitted when `indexer` close an allocation in `epoch` for `allocationID`. * An amount of `tokens` get unallocated from `subgraphDeploymentID`. * The `effectiveAllocation` are the tokens allocated from creation to closing. * This event also emits the POI (proof of indexing) submitted by the indexer. * `isPublic` is true if the sender was someone other than the indexer. */ event AllocationClosed( address indexed indexer, bytes32 indexed subgraphDeploymentID, uint256 epoch, uint256 tokens, address indexed allocationID, uint256 effectiveAllocation, address sender, bytes32 poi, bool isPublic ); /** * @dev Emitted when `indexer` claimed a rebate on `subgraphDeploymentID` during `epoch` * related to the `forEpoch` rebate pool. * The rebate is for `tokens` amount and `unclaimedAllocationsCount` are left for claim * in the rebate pool. `delegationFees` collected and sent to delegation pool. */ event RebateClaimed( address indexed indexer, bytes32 indexed subgraphDeploymentID, address indexed allocationID, uint256 epoch, uint256 forEpoch, uint256 tokens, uint256 unclaimedAllocationsCount, uint256 delegationFees ); /** * @dev Emitted when `caller` set `slasher` address as `allowed` to slash stakes. */ event SlasherUpdate(address indexed caller, address indexed slasher, bool allowed); /** * @dev Emitted when `caller` set `assetHolder` address as `allowed` to send funds * to staking contract. */ event AssetHolderUpdate(address indexed caller, address indexed assetHolder, bool allowed); /** * @dev Emitted when `indexer` set `operator` access. */ event SetOperator(address indexed indexer, address indexed operator, bool allowed); /** * @dev Emitted when `indexer` set an address to receive rewards. */ event SetRewardsDestination(address indexed indexer, address indexed destination); /** * @dev Check if the caller is the slasher. */ modifier onlySlasher() { require(slashers[msg.sender] == true, "!slasher"); _; } /** * @dev Check if the caller is authorized (indexer or operator) */ function _isAuth(address _indexer) private view returns (bool) { return msg.sender == _indexer || isOperator(msg.sender, _indexer) == true; } /** * @dev Initialize this contract. */ function initialize( address _controller, uint256 _minimumIndexerStake, uint32 _thawingPeriod, uint32 _protocolPercentage, uint32 _curationPercentage, uint32 _channelDisputeEpochs, uint32 _maxAllocationEpochs, uint32 _delegationUnbondingPeriod, uint32 _delegationRatio, uint32 _rebateAlphaNumerator, uint32 _rebateAlphaDenominator ) external onlyImpl { Managed._initialize(_controller); // Settings _setMinimumIndexerStake(_minimumIndexerStake); _setThawingPeriod(_thawingPeriod); _setProtocolPercentage(_protocolPercentage); _setCurationPercentage(_curationPercentage); _setChannelDisputeEpochs(_channelDisputeEpochs); _setMaxAllocationEpochs(_maxAllocationEpochs); _setDelegationUnbondingPeriod(_delegationUnbondingPeriod); _setDelegationRatio(_delegationRatio); _setDelegationParametersCooldown(0); _setDelegationTaxPercentage(0); _setRebateRatio(_rebateAlphaNumerator, _rebateAlphaDenominator); } /** * @dev Set the minimum indexer stake required to. * @param _minimumIndexerStake Minimum indexer stake */ function setMinimumIndexerStake(uint256 _minimumIndexerStake) external override onlyGovernor { _setMinimumIndexerStake(_minimumIndexerStake); } /** * @dev Internal: Set the minimum indexer stake required. * @param _minimumIndexerStake Minimum indexer stake */ function _setMinimumIndexerStake(uint256 _minimumIndexerStake) private { require(_minimumIndexerStake > 0, "!minimumIndexerStake"); minimumIndexerStake = _minimumIndexerStake; emit ParameterUpdated("minimumIndexerStake"); } /** * @dev Set the thawing period for unstaking. * @param _thawingPeriod Period in blocks to wait for token withdrawals after unstaking */ function setThawingPeriod(uint32 _thawingPeriod) external override onlyGovernor { _setThawingPeriod(_thawingPeriod); } /** * @dev Internal: Set the thawing period for unstaking. * @param _thawingPeriod Period in blocks to wait for token withdrawals after unstaking */ function _setThawingPeriod(uint32 _thawingPeriod) private { require(_thawingPeriod > 0, "!thawingPeriod"); thawingPeriod = _thawingPeriod; emit ParameterUpdated("thawingPeriod"); } /** * @dev Set the curation percentage of query fees sent to curators. * @param _percentage Percentage of query fees sent to curators */ function setCurationPercentage(uint32 _percentage) external override onlyGovernor { _setCurationPercentage(_percentage); } /** * @dev Internal: Set the curation percentage of query fees sent to curators. * @param _percentage Percentage of query fees sent to curators */ function _setCurationPercentage(uint32 _percentage) private { // Must be within 0% to 100% (inclusive) require(_percentage <= MAX_PPM, ">percentage"); curationPercentage = _percentage; emit ParameterUpdated("curationPercentage"); } /** * @dev Set a protocol percentage to burn when collecting query fees. * @param _percentage Percentage of query fees to burn as protocol fee */ function setProtocolPercentage(uint32 _percentage) external override onlyGovernor { _setProtocolPercentage(_percentage); } /** * @dev Internal: Set a protocol percentage to burn when collecting query fees. * @param _percentage Percentage of query fees to burn as protocol fee */ function _setProtocolPercentage(uint32 _percentage) private { // Must be within 0% to 100% (inclusive) require(_percentage <= MAX_PPM, ">percentage"); protocolPercentage = _percentage; emit ParameterUpdated("protocolPercentage"); } /** * @dev Set the period in epochs that need to pass before fees in rebate pool can be claimed. * @param _channelDisputeEpochs Period in epochs */ function setChannelDisputeEpochs(uint32 _channelDisputeEpochs) external override onlyGovernor { _setChannelDisputeEpochs(_channelDisputeEpochs); } /** * @dev Internal: Set the period in epochs that need to pass before fees in rebate pool can be claimed. * @param _channelDisputeEpochs Period in epochs */ function _setChannelDisputeEpochs(uint32 _channelDisputeEpochs) private { require(_channelDisputeEpochs > 0, "!channelDisputeEpochs"); channelDisputeEpochs = _channelDisputeEpochs; emit ParameterUpdated("channelDisputeEpochs"); } /** * @dev Set the max time allowed for indexers stake on allocations. * @param _maxAllocationEpochs Allocation duration limit in epochs */ function setMaxAllocationEpochs(uint32 _maxAllocationEpochs) external override onlyGovernor { _setMaxAllocationEpochs(_maxAllocationEpochs); } /** * @dev Internal: Set the max time allowed for indexers stake on allocations. * @param _maxAllocationEpochs Allocation duration limit in epochs */ function _setMaxAllocationEpochs(uint32 _maxAllocationEpochs) private { maxAllocationEpochs = _maxAllocationEpochs; emit ParameterUpdated("maxAllocationEpochs"); } /** * @dev Set the rebate ratio (fees to allocated stake). * @param _alphaNumerator Numerator of `alpha` in the cobb-douglas function * @param _alphaDenominator Denominator of `alpha` in the cobb-douglas function */ function setRebateRatio(uint32 _alphaNumerator, uint32 _alphaDenominator) external override onlyGovernor { _setRebateRatio(_alphaNumerator, _alphaDenominator); } /** * @dev Set the rebate ratio (fees to allocated stake). * @param _alphaNumerator Numerator of `alpha` in the cobb-douglas function * @param _alphaDenominator Denominator of `alpha` in the cobb-douglas function */ function _setRebateRatio(uint32 _alphaNumerator, uint32 _alphaDenominator) private { require(_alphaNumerator > 0 && _alphaDenominator > 0, "!alpha"); alphaNumerator = _alphaNumerator; alphaDenominator = _alphaDenominator; emit ParameterUpdated("rebateRatio"); } /** * @dev Set the delegation ratio. * If set to 10 it means the indexer can use up to 10x the indexer staked amount * from their delegated tokens * @param _delegationRatio Delegation capacity multiplier */ function setDelegationRatio(uint32 _delegationRatio) external override onlyGovernor { _setDelegationRatio(_delegationRatio); } /** * @dev Internal: Set the delegation ratio. * If set to 10 it means the indexer can use up to 10x the indexer staked amount * from their delegated tokens * @param _delegationRatio Delegation capacity multiplier */ function _setDelegationRatio(uint32 _delegationRatio) private { delegationRatio = _delegationRatio; emit ParameterUpdated("delegationRatio"); } /** * @dev Set the delegation parameters for the caller. * @param _indexingRewardCut Percentage of indexing rewards left for delegators * @param _queryFeeCut Percentage of query fees left for delegators * @param _cooldownBlocks Period that need to pass to update delegation parameters */ function setDelegationParameters( uint32 _indexingRewardCut, uint32 _queryFeeCut, uint32 _cooldownBlocks ) public override { _setDelegationParameters(msg.sender, _indexingRewardCut, _queryFeeCut, _cooldownBlocks); } /** * @dev Set the delegation parameters for a particular indexer. * @param _indexer Indexer to set delegation parameters * @param _indexingRewardCut Percentage of indexing rewards left for delegators * @param _queryFeeCut Percentage of query fees left for delegators * @param _cooldownBlocks Period that need to pass to update delegation parameters */ function _setDelegationParameters( address _indexer, uint32 _indexingRewardCut, uint32 _queryFeeCut, uint32 _cooldownBlocks ) private { // Incentives must be within bounds require(_queryFeeCut <= MAX_PPM, ">queryFeeCut"); require(_indexingRewardCut <= MAX_PPM, ">indexingRewardCut"); // Cooldown period set by indexer cannot be below protocol global setting require(_cooldownBlocks >= delegationParametersCooldown, "<cooldown"); // Verify the cooldown period passed DelegationPool storage pool = delegationPools[_indexer]; require( pool.updatedAtBlock == 0 || pool.updatedAtBlock.add(uint256(pool.cooldownBlocks)) <= block.number, "!cooldown" ); // Update delegation params pool.indexingRewardCut = _indexingRewardCut; pool.queryFeeCut = _queryFeeCut; pool.cooldownBlocks = _cooldownBlocks; pool.updatedAtBlock = block.number; emit DelegationParametersUpdated( _indexer, _indexingRewardCut, _queryFeeCut, _cooldownBlocks ); } /** * @dev Set the time in blocks an indexer needs to wait to change delegation parameters. * @param _blocks Number of blocks to set the delegation parameters cooldown period */ function setDelegationParametersCooldown(uint32 _blocks) external override onlyGovernor { _setDelegationParametersCooldown(_blocks); } /** * @dev Internal: Set the time in blocks an indexer needs to wait to change delegation parameters. * @param _blocks Number of blocks to set the delegation parameters cooldown period */ function _setDelegationParametersCooldown(uint32 _blocks) private { delegationParametersCooldown = _blocks; emit ParameterUpdated("delegationParametersCooldown"); } /** * @dev Set the period for undelegation of stake from indexer. * @param _delegationUnbondingPeriod Period in epochs to wait for token withdrawals after undelegating */ function setDelegationUnbondingPeriod(uint32 _delegationUnbondingPeriod) external override onlyGovernor { _setDelegationUnbondingPeriod(_delegationUnbondingPeriod); } /** * @dev Internal: Set the period for undelegation of stake from indexer. * @param _delegationUnbondingPeriod Period in epochs to wait for token withdrawals after undelegating */ function _setDelegationUnbondingPeriod(uint32 _delegationUnbondingPeriod) private { require(_delegationUnbondingPeriod > 0, "!delegationUnbondingPeriod"); delegationUnbondingPeriod = _delegationUnbondingPeriod; emit ParameterUpdated("delegationUnbondingPeriod"); } /** * @dev Set a delegation tax percentage to burn when delegated funds are deposited. * @param _percentage Percentage of delegated tokens to burn as delegation tax */ function setDelegationTaxPercentage(uint32 _percentage) external override onlyGovernor { _setDelegationTaxPercentage(_percentage); } /** * @dev Internal: Set a delegation tax percentage to burn when delegated funds are deposited. * @param _percentage Percentage of delegated tokens to burn as delegation tax */ function _setDelegationTaxPercentage(uint32 _percentage) private { // Must be within 0% to 100% (inclusive) require(_percentage <= MAX_PPM, ">percentage"); delegationTaxPercentage = _percentage; emit ParameterUpdated("delegationTaxPercentage"); } /** * @dev Set or unset an address as allowed slasher. * @param _slasher Address of the party allowed to slash indexers * @param _allowed True if slasher is allowed */ function setSlasher(address _slasher, bool _allowed) external override onlyGovernor { require(_slasher != address(0), "!slasher"); slashers[_slasher] = _allowed; emit SlasherUpdate(msg.sender, _slasher, _allowed); } /** * @dev Set an address as allowed asset holder. * @param _assetHolder Address of allowed source for state channel funds * @param _allowed True if asset holder is allowed */ function setAssetHolder(address _assetHolder, bool _allowed) external override onlyGovernor { require(_assetHolder != address(0), "!assetHolder"); assetHolders[_assetHolder] = _allowed; emit AssetHolderUpdate(msg.sender, _assetHolder, _allowed); } /** * @dev Return if allocationID is used. * @param _allocationID Address used as signer by the indexer for an allocation * @return True if allocationID already used */ function isAllocation(address _allocationID) external view override returns (bool) { return _getAllocationState(_allocationID) != AllocationState.Null; } /** * @dev Getter that returns if an indexer has any stake. * @param _indexer Address of the indexer * @return True if indexer has staked tokens */ function hasStake(address _indexer) external view override returns (bool) { return stakes[_indexer].tokensStaked > 0; } /** * @dev Return the allocation by ID. * @param _allocationID Address used as allocation identifier * @return Allocation data */ function getAllocation(address _allocationID) external view override returns (Allocation memory) { return allocations[_allocationID]; } /** * @dev Return the current state of an allocation. * @param _allocationID Address used as the allocation identifier * @return AllocationState */ function getAllocationState(address _allocationID) external view override returns (AllocationState) { return _getAllocationState(_allocationID); } /** * @dev Return the total amount of tokens allocated to subgraph. * @param _subgraphDeploymentID Address used as the allocation identifier * @return Total tokens allocated to subgraph */ function getSubgraphAllocatedTokens(bytes32 _subgraphDeploymentID) external view override returns (uint256) { return subgraphAllocations[_subgraphDeploymentID]; } /** * @dev Return the delegation from a delegator to an indexer. * @param _indexer Address of the indexer where funds have been delegated * @param _delegator Address of the delegator * @return Delegation data */ function getDelegation(address _indexer, address _delegator) external view override returns (Delegation memory) { return delegationPools[_indexer].delegators[_delegator]; } /** * @dev Return whether the delegator has delegated to the indexer. * @param _indexer Address of the indexer where funds have been delegated * @param _delegator Address of the delegator * @return True if delegator of indexer */ function isDelegator(address _indexer, address _delegator) public view override returns (bool) { return delegationPools[_indexer].delegators[_delegator].shares > 0; } /** * @dev Get the total amount of tokens staked by the indexer. * @param _indexer Address of the indexer * @return Amount of tokens staked by the indexer */ function getIndexerStakedTokens(address _indexer) external view override returns (uint256) { return stakes[_indexer].tokensStaked; } /** * @dev Get the total amount of tokens available to use in allocations. * This considers the indexer stake and delegated tokens according to delegation ratio * @param _indexer Address of the indexer * @return Amount of tokens staked by the indexer */ function getIndexerCapacity(address _indexer) public view override returns (uint256) { Stakes.Indexer memory indexerStake = stakes[_indexer]; uint256 tokensDelegated = delegationPools[_indexer].tokens; uint256 tokensDelegatedCap = indexerStake.tokensSecureStake().mul(uint256(delegationRatio)); uint256 tokensDelegatedCapacity = MathUtils.min(tokensDelegated, tokensDelegatedCap); return indexerStake.tokensAvailableWithDelegation(tokensDelegatedCapacity); } /** * @dev Returns amount of delegated tokens ready to be withdrawn after unbonding period. * @param _delegation Delegation of tokens from delegator to indexer * @return Amount of tokens to withdraw */ function getWithdraweableDelegatedTokens(Delegation memory _delegation) public view returns (uint256) { // There must be locked tokens and period passed uint256 currentEpoch = epochManager().currentEpoch(); if (_delegation.tokensLockedUntil > 0 && currentEpoch >= _delegation.tokensLockedUntil) { return _delegation.tokensLocked; } return 0; } /** * @dev Authorize or unauthorize an address to be an operator. * @param _operator Address to authorize * @param _allowed Whether authorized or not */ function setOperator(address _operator, bool _allowed) external override { require(_operator != msg.sender, "operator == sender"); operatorAuth[msg.sender][_operator] = _allowed; emit SetOperator(msg.sender, _operator, _allowed); } /** * @dev Return true if operator is allowed for indexer. * @param _operator Address of the operator * @param _indexer Address of the indexer */ function isOperator(address _operator, address _indexer) public view override returns (bool) { return operatorAuth[_indexer][_operator]; } /** * @dev Deposit tokens on the indexer stake. * @param _tokens Amount of tokens to stake */ function stake(uint256 _tokens) external override { stakeTo(msg.sender, _tokens); } /** * @dev Deposit tokens on the indexer stake. * @param _indexer Address of the indexer * @param _tokens Amount of tokens to stake */ function stakeTo(address _indexer, uint256 _tokens) public override notPartialPaused { require(_tokens > 0, "!tokens"); // Ensure minimum stake require( stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, "!minimumIndexerStake" ); // Transfer tokens to stake from caller to this contract TokenUtils.pullTokens(graphToken(), msg.sender, _tokens); // Stake the transferred tokens _stake(_indexer, _tokens); } /** * @dev Unstake tokens from the indexer stake, lock them until thawing period expires. * NOTE: The function accepts an amount greater than the currently staked tokens. * If that happens, it will try to unstake the max amount of tokens it can. * The reason for this behaviour is to avoid time conditions while the transaction * is in flight. * @param _tokens Amount of tokens to unstake */ function unstake(uint256 _tokens) external override notPartialPaused { address indexer = msg.sender; Stakes.Indexer storage indexerStake = stakes[indexer]; require(indexerStake.tokensStaked > 0, "!stake"); // Tokens to lock is capped to the available tokens uint256 tokensToLock = MathUtils.min(indexerStake.tokensAvailable(), _tokens); require(tokensToLock > 0, "!stake-avail"); // Ensure minimum stake uint256 newStake = indexerStake.tokensSecureStake().sub(tokensToLock); require(newStake == 0 || newStake >= minimumIndexerStake, "!minimumIndexerStake"); // Before locking more tokens, withdraw any unlocked ones if possible uint256 tokensToWithdraw = indexerStake.tokensWithdrawable(); if (tokensToWithdraw > 0) { _withdraw(indexer); } // Update the indexer stake locking tokens indexerStake.lockTokens(tokensToLock, thawingPeriod); emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil); } /** * @dev Withdraw indexer tokens once the thawing period has passed. */ function withdraw() external override notPaused { _withdraw(msg.sender); } /** * @dev Set the destination where to send rewards. * @param _destination Rewards destination address. If set to zero, rewards will be restaked */ function setRewardsDestination(address _destination) external override { rewardsDestination[msg.sender] = _destination; emit SetRewardsDestination(msg.sender, _destination); } /** * @dev Slash the indexer stake. Delegated tokens are not subject to slashing. * Can only be called by the slasher role. * @param _indexer Address of indexer to slash * @param _tokens Amount of tokens to slash from the indexer stake * @param _reward Amount of reward tokens to send to a beneficiary * @param _beneficiary Address of a beneficiary to receive a reward for the slashing */ function slash( address _indexer, uint256 _tokens, uint256 _reward, address _beneficiary ) external override onlySlasher notPartialPaused { Stakes.Indexer storage indexerStake = stakes[_indexer]; // Only able to slash a non-zero number of tokens require(_tokens > 0, "!tokens"); // Rewards comes from tokens slashed balance require(_tokens >= _reward, "rewards>slash"); // Cannot slash stake of an indexer without any or enough stake require(indexerStake.tokensStaked > 0, "!stake"); require(_tokens <= indexerStake.tokensStaked, "slash>stake"); // Validate beneficiary of slashed tokens require(_beneficiary != address(0), "!beneficiary"); // Slashing more tokens than freely available (over allocation condition) // Unlock locked tokens to avoid the indexer to withdraw them if (_tokens > indexerStake.tokensAvailable() && indexerStake.tokensLocked > 0) { uint256 tokensOverAllocated = _tokens.sub(indexerStake.tokensAvailable()); uint256 tokensToUnlock = MathUtils.min(tokensOverAllocated, indexerStake.tokensLocked); indexerStake.unlockTokens(tokensToUnlock); } // Remove tokens to slash from the stake indexerStake.release(_tokens); // -- Interactions -- IGraphToken graphToken = graphToken(); // Set apart the reward for the beneficiary and burn remaining slashed stake TokenUtils.burnTokens(graphToken, _tokens.sub(_reward)); // Give the beneficiary a reward for slashing TokenUtils.pushTokens(graphToken, _beneficiary, _reward); emit StakeSlashed(_indexer, _tokens, _reward, _beneficiary); } /** * @dev Delegate tokens to an indexer. * @param _indexer Address of the indexer to delegate tokens to * @param _tokens Amount of tokens to delegate * @return Amount of shares issued of the delegation pool */ function delegate(address _indexer, uint256 _tokens) external override notPartialPaused returns (uint256) { address delegator = msg.sender; // Transfer tokens to delegate to this contract TokenUtils.pullTokens(graphToken(), delegator, _tokens); // Update state return _delegate(delegator, _indexer, _tokens); } /** * @dev Undelegate tokens from an indexer. * @param _indexer Address of the indexer where tokens had been delegated * @param _shares Amount of shares to return and undelegate tokens * @return Amount of tokens returned for the shares of the delegation pool */ function undelegate(address _indexer, uint256 _shares) external override notPartialPaused returns (uint256) { return _undelegate(msg.sender, _indexer, _shares); } /** * @dev Withdraw delegated tokens once the unbonding period has passed. * @param _indexer Withdraw available tokens delegated to indexer * @param _delegateToIndexer Re-delegate to indexer address if non-zero, withdraw if zero address */ function withdrawDelegated(address _indexer, address _delegateToIndexer) external override notPaused returns (uint256) { return _withdrawDelegated(msg.sender, _indexer, _delegateToIndexer); } /** * @dev Allocate available tokens to a subgraph deployment. * @param _subgraphDeploymentID ID of the SubgraphDeployment where tokens will be allocated * @param _tokens Amount of tokens to allocate * @param _allocationID The allocation identifier * @param _metadata IPFS hash for additional information about the allocation * @param _proof A 65-bytes Ethereum signed message of `keccak256(indexerAddress,allocationID)` */ function allocate( bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external override notPaused { _allocate(msg.sender, _subgraphDeploymentID, _tokens, _allocationID, _metadata, _proof); } /** * @dev Allocate available tokens to a subgraph deployment. * @param _indexer Indexer address to allocate funds from. * @param _subgraphDeploymentID ID of the SubgraphDeployment where tokens will be allocated * @param _tokens Amount of tokens to allocate * @param _allocationID The allocation identifier * @param _metadata IPFS hash for additional information about the allocation * @param _proof A 65-bytes Ethereum signed message of `keccak256(indexerAddress,allocationID)` */ function allocateFrom( address _indexer, bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external override notPaused { _allocate(_indexer, _subgraphDeploymentID, _tokens, _allocationID, _metadata, _proof); } /** * @dev Close an allocation and free the staked tokens. * To be eligible for rewards a proof of indexing must be presented. * Presenting a bad proof is subject to slashable condition. * To opt out for rewards set _poi to 0x0 * @param _allocationID The allocation identifier * @param _poi Proof of indexing submitted for the allocated period */ function closeAllocation(address _allocationID, bytes32 _poi) external override notPaused { _closeAllocation(_allocationID, _poi); } /** * @dev Close multiple allocations and free the staked tokens. * To be eligible for rewards a proof of indexing must be presented. * Presenting a bad proof is subject to slashable condition. * To opt out for rewards set _poi to 0x0 * @param _requests An array of CloseAllocationRequest */ function closeAllocationMany(CloseAllocationRequest[] calldata _requests) external override notPaused { for (uint256 i = 0; i < _requests.length; i++) { _closeAllocation(_requests[i].allocationID, _requests[i].poi); } } /** * @dev Close and allocate. This will perform a close and then create a new Allocation * atomically on the same transaction. * @param _closingAllocationID The identifier of the allocation to be closed * @param _poi Proof of indexing submitted for the allocated period * @param _indexer Indexer address to allocate funds from. * @param _subgraphDeploymentID ID of the SubgraphDeployment where tokens will be allocated * @param _tokens Amount of tokens to allocate * @param _allocationID The allocation identifier * @param _metadata IPFS hash for additional information about the allocation * @param _proof A 65-bytes Ethereum signed message of `keccak256(indexerAddress,allocationID)` */ function closeAndAllocate( address _closingAllocationID, bytes32 _poi, address _indexer, bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external override notPaused { _closeAllocation(_closingAllocationID, _poi); _allocate(_indexer, _subgraphDeploymentID, _tokens, _allocationID, _metadata, _proof); } /** * @dev Collect query fees from state channels and assign them to an allocation. * Funds received are only accepted from a valid sender. * To avoid reverting on the withdrawal from channel flow this function will: * 1) Accept calls with zero tokens. * 2) Accept calls after an allocation passed the dispute period, in that case, all * the received tokens are burned. * @param _tokens Amount of tokens to collect * @param _allocationID Allocation where the tokens will be assigned */ function collect(uint256 _tokens, address _allocationID) external override { // Allocation identifier validation require(_allocationID != address(0), "!alloc"); // The contract caller must be an authorized asset holder require(assetHolders[msg.sender] == true, "!assetHolder"); // Allocation must exist AllocationState allocState = _getAllocationState(_allocationID); require(allocState != AllocationState.Null, "!collect"); // Get allocation Allocation storage alloc = allocations[_allocationID]; uint256 queryFees = _tokens; uint256 curationFees = 0; bytes32 subgraphDeploymentID = alloc.subgraphDeploymentID; // Process query fees only if non-zero amount if (queryFees > 0) { // Pull tokens to collect from the authorized sender IGraphToken graphToken = graphToken(); TokenUtils.pullTokens(graphToken, msg.sender, _tokens); // -- Collect protocol tax -- // If the Allocation is not active or closed we are going to charge a 100% protocol tax uint256 usedProtocolPercentage = (allocState == AllocationState.Active || allocState == AllocationState.Closed) ? protocolPercentage : MAX_PPM; uint256 protocolTax = _collectTax(graphToken, queryFees, usedProtocolPercentage); queryFees = queryFees.sub(protocolTax); // -- Collect curation fees -- // Only if the subgraph deployment is curated curationFees = _collectCurationFees( graphToken, subgraphDeploymentID, queryFees, curationPercentage ); queryFees = queryFees.sub(curationFees); // Add funds to the allocation alloc.collectedFees = alloc.collectedFees.add(queryFees); // When allocation is closed redirect funds to the rebate pool // This way we can keep collecting tokens even after the allocation is closed and // before it gets to the finalized state. if (allocState == AllocationState.Closed) { Rebates.Pool storage rebatePool = rebates[alloc.closedAtEpoch]; rebatePool.fees = rebatePool.fees.add(queryFees); } } emit AllocationCollected( alloc.indexer, subgraphDeploymentID, epochManager().currentEpoch(), _tokens, _allocationID, msg.sender, curationFees, queryFees ); } /** * @dev Claim tokens from the rebate pool. * @param _allocationID Allocation from where we are claiming tokens * @param _restake True if restake fees instead of transfer to indexer */ function claim(address _allocationID, bool _restake) external override notPaused { _claim(_allocationID, _restake); } /** * @dev Claim tokens from the rebate pool for many allocations. * @param _allocationID Array of allocations from where we are claiming tokens * @param _restake True if restake fees instead of transfer to indexer */ function claimMany(address[] calldata _allocationID, bool _restake) external override notPaused { for (uint256 i = 0; i < _allocationID.length; i++) { _claim(_allocationID[i], _restake); } } /** * @dev Stake tokens on the indexer. * This function does not check minimum indexer stake requirement to allow * to be called by functions that increase the stake when collecting rewards * without reverting * @param _indexer Address of staking party * @param _tokens Amount of tokens to stake */ function _stake(address _indexer, uint256 _tokens) private { // Deposit tokens into the indexer stake stakes[_indexer].deposit(_tokens); // Initialize the delegation pool the first time if (delegationPools[_indexer].updatedAtBlock == 0) { _setDelegationParameters(_indexer, MAX_PPM, MAX_PPM, delegationParametersCooldown); } emit StakeDeposited(_indexer, _tokens); } /** * @dev Withdraw indexer tokens once the thawing period has passed. * @param _indexer Address of indexer to withdraw funds from */ function _withdraw(address _indexer) private { // Get tokens available for withdraw and update balance uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens(); require(tokensToWithdraw > 0, "!tokens"); // Return tokens to the indexer TokenUtils.pushTokens(graphToken(), _indexer, tokensToWithdraw); emit StakeWithdrawn(_indexer, tokensToWithdraw); } /** * @dev Allocate available tokens to a subgraph deployment. * @param _indexer Indexer address to allocate funds from. * @param _subgraphDeploymentID ID of the SubgraphDeployment where tokens will be allocated * @param _tokens Amount of tokens to allocate * @param _allocationID The allocationID will work to identify collected funds related to this allocation * @param _metadata Metadata related to the allocation * @param _proof A 65-bytes Ethereum signed message of `keccak256(indexerAddress,allocationID)` */ function _allocate( address _indexer, bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) private { require(_isAuth(_indexer), "!auth"); // Check allocation require(_allocationID != address(0), "!alloc"); require(_getAllocationState(_allocationID) == AllocationState.Null, "!null"); // Caller must prove that they own the private key for the allocationID adddress // The proof is an Ethereum signed message of KECCAK256(indexerAddress,allocationID) bytes32 messageHash = keccak256(abi.encodePacked(_indexer, _allocationID)); bytes32 digest = ECDSA.toEthSignedMessageHash(messageHash); require(ECDSA.recover(digest, _proof) == _allocationID, "!proof"); if (_tokens > 0) { // Needs to have free capacity not used for other purposes to allocate require(getIndexerCapacity(_indexer) >= _tokens, "!capacity"); } else { // Allocating zero-tokens still needs to comply with stake requirements require( stakes[_indexer].tokensSecureStake() >= minimumIndexerStake, "!minimumIndexerStake" ); } // Creates an allocation // Allocation identifiers are not reused // The assetHolder address can send collected funds to the allocation Allocation memory alloc = Allocation( _indexer, _subgraphDeploymentID, _tokens, // Tokens allocated epochManager().currentEpoch(), // createdAtEpoch 0, // closedAtEpoch 0, // Initialize collected fees 0, // Initialize effective allocation (_tokens > 0) ? _updateRewards(_subgraphDeploymentID) : 0 // Initialize accumulated rewards per stake allocated ); allocations[_allocationID] = alloc; // -- Rewards Distribution -- // Process non-zero-allocation rewards tracking if (_tokens > 0) { // Mark allocated tokens as used stakes[_indexer].allocate(alloc.tokens); // Track total allocations per subgraph // Used for rewards calculations subgraphAllocations[alloc.subgraphDeploymentID] = subgraphAllocations[ alloc.subgraphDeploymentID ].add(alloc.tokens); } emit AllocationCreated( _indexer, _subgraphDeploymentID, alloc.createdAtEpoch, alloc.tokens, _allocationID, _metadata ); } /** * @dev Close an allocation and free the staked tokens. * @param _allocationID The allocation identifier * @param _poi Proof of indexing submitted for the allocated period */ function _closeAllocation(address _allocationID, bytes32 _poi) private { // Allocation must exist and be active AllocationState allocState = _getAllocationState(_allocationID); require(allocState == AllocationState.Active, "!active"); // Get allocation Allocation memory alloc = allocations[_allocationID]; // Validate that an allocation cannot be closed before one epoch alloc.closedAtEpoch = epochManager().currentEpoch(); uint256 epochs = MathUtils.diffOrZero(alloc.closedAtEpoch, alloc.createdAtEpoch); require(epochs > 0, "<epochs"); // Indexer or operator can close an allocation // Anyone is allowed to close ONLY under two concurrent conditions // - After maxAllocationEpochs passed // - When the allocation is for non-zero amount of tokens bool isIndexer = _isAuth(alloc.indexer); if (epochs <= maxAllocationEpochs || alloc.tokens == 0) { require(isIndexer, "!auth"); } // Close the allocation and start counting a period to settle remaining payments from // state channels. allocations[_allocationID].closedAtEpoch = alloc.closedAtEpoch; // -- Rebate Pool -- // Calculate effective allocation for the amount of epochs it remained allocated alloc.effectiveAllocation = _getEffectiveAllocation( maxAllocationEpochs, alloc.tokens, epochs ); allocations[_allocationID].effectiveAllocation = alloc.effectiveAllocation; // Account collected fees and effective allocation in rebate pool for the epoch Rebates.Pool storage rebatePool = rebates[alloc.closedAtEpoch]; if (!rebatePool.exists()) { rebatePool.init(alphaNumerator, alphaDenominator); } rebatePool.addToPool(alloc.collectedFees, alloc.effectiveAllocation); // -- Rewards Distribution -- // Process non-zero-allocation rewards tracking if (alloc.tokens > 0) { // Distribute rewards if proof of indexing was presented by the indexer or operator if (isIndexer && _poi != 0) { _distributeRewards(_allocationID, alloc.indexer); } else { _updateRewards(alloc.subgraphDeploymentID); } // Free allocated tokens from use stakes[alloc.indexer].unallocate(alloc.tokens); // Track total allocations per subgraph // Used for rewards calculations subgraphAllocations[alloc.subgraphDeploymentID] = subgraphAllocations[ alloc.subgraphDeploymentID ].sub(alloc.tokens); } emit AllocationClosed( alloc.indexer, alloc.subgraphDeploymentID, alloc.closedAtEpoch, alloc.tokens, _allocationID, alloc.effectiveAllocation, msg.sender, _poi, !isIndexer ); } /** * @dev Claim tokens from the rebate pool. * @param _allocationID Allocation from where we are claiming tokens * @param _restake True if restake fees instead of transfer to indexer */ function _claim(address _allocationID, bool _restake) private { // Funds can only be claimed after a period of time passed since allocation was closed AllocationState allocState = _getAllocationState(_allocationID); require(allocState == AllocationState.Finalized, "!finalized"); // Get allocation Allocation memory alloc = allocations[_allocationID]; // Only the indexer or operator can decide if to restake bool restake = _isAuth(alloc.indexer) ? _restake : false; // Process rebate reward Rebates.Pool storage rebatePool = rebates[alloc.closedAtEpoch]; uint256 tokensToClaim = rebatePool.redeem(alloc.collectedFees, alloc.effectiveAllocation); // Add delegation rewards to the delegation pool uint256 delegationRewards = _collectDelegationQueryRewards(alloc.indexer, tokensToClaim); tokensToClaim = tokensToClaim.sub(delegationRewards); // Purge allocation data except for: // - indexer: used in disputes and to avoid reusing an allocationID // - subgraphDeploymentID: used in disputes allocations[_allocationID].tokens = 0; allocations[_allocationID].createdAtEpoch = 0; // This avoid collect(), close() and claim() to be called allocations[_allocationID].closedAtEpoch = 0; allocations[_allocationID].collectedFees = 0; allocations[_allocationID].effectiveAllocation = 0; allocations[_allocationID].accRewardsPerAllocatedToken = 0; // -- Interactions -- IGraphToken graphToken = graphToken(); // When all allocations processed then burn unclaimed fees and prune rebate pool if (rebatePool.unclaimedAllocationsCount == 0) { TokenUtils.burnTokens(graphToken, rebatePool.unclaimedFees()); delete rebates[alloc.closedAtEpoch]; } // When there are tokens to claim from the rebate pool, transfer or restake _sendRewards(graphToken, tokensToClaim, alloc.indexer, restake); emit RebateClaimed( alloc.indexer, alloc.subgraphDeploymentID, _allocationID, epochManager().currentEpoch(), alloc.closedAtEpoch, tokensToClaim, rebatePool.unclaimedAllocationsCount, delegationRewards ); } /** * @dev Delegate tokens to an indexer. * @param _delegator Address of the delegator * @param _indexer Address of the indexer to delegate tokens to * @param _tokens Amount of tokens to delegate * @return Amount of shares issued of the delegation pool */ function _delegate( address _delegator, address _indexer, uint256 _tokens ) private returns (uint256) { // Only delegate a non-zero amount of tokens require(_tokens > 0, "!tokens"); // Only delegate to non-empty address require(_indexer != address(0), "!indexer"); // Only delegate to staked indexer require(stakes[_indexer].tokensStaked > 0, "!stake"); // Get the delegation pool of the indexer DelegationPool storage pool = delegationPools[_indexer]; Delegation storage delegation = pool.delegators[_delegator]; // Collect delegation tax uint256 delegationTax = _collectTax(graphToken(), _tokens, delegationTaxPercentage); uint256 delegatedTokens = _tokens.sub(delegationTax); // Calculate shares to issue uint256 shares = (pool.tokens == 0) ? delegatedTokens : delegatedTokens.mul(pool.shares).div(pool.tokens); require(shares > 0, "!shares"); // Update the delegation pool pool.tokens = pool.tokens.add(delegatedTokens); pool.shares = pool.shares.add(shares); // Update the individual delegation delegation.shares = delegation.shares.add(shares); emit StakeDelegated(_indexer, _delegator, delegatedTokens, shares); return shares; } /** * @dev Undelegate tokens from an indexer. * @param _delegator Address of the delegator * @param _indexer Address of the indexer where tokens had been delegated * @param _shares Amount of shares to return and undelegate tokens * @return Amount of tokens returned for the shares of the delegation pool */ function _undelegate( address _delegator, address _indexer, uint256 _shares ) private returns (uint256) { // Can only undelegate a non-zero amount of shares require(_shares > 0, "!shares"); // Get the delegation pool of the indexer DelegationPool storage pool = delegationPools[_indexer]; Delegation storage delegation = pool.delegators[_delegator]; // Delegator need to have enough shares in the pool to undelegate require(delegation.shares >= _shares, "!shares-avail"); // Withdraw tokens if available if (getWithdraweableDelegatedTokens(delegation) > 0) { _withdrawDelegated(_delegator, _indexer, address(0)); } // Calculate tokens to get in exchange for the shares uint256 tokens = _shares.mul(pool.tokens).div(pool.shares); // Update the delegation pool pool.tokens = pool.tokens.sub(tokens); pool.shares = pool.shares.sub(_shares); // Update the delegation delegation.shares = delegation.shares.sub(_shares); delegation.tokensLocked = delegation.tokensLocked.add(tokens); delegation.tokensLockedUntil = epochManager().currentEpoch().add(delegationUnbondingPeriod); emit StakeDelegatedLocked( _indexer, _delegator, tokens, _shares, delegation.tokensLockedUntil ); return tokens; } /** * @dev Withdraw delegated tokens once the unbonding period has passed. * @param _delegator Delegator that is withdrawing tokens * @param _indexer Withdraw available tokens delegated to indexer * @param _delegateToIndexer Re-delegate to indexer address if non-zero, withdraw if zero address */ function _withdrawDelegated( address _delegator, address _indexer, address _delegateToIndexer ) private returns (uint256) { // Get the delegation pool of the indexer DelegationPool storage pool = delegationPools[_indexer]; Delegation storage delegation = pool.delegators[_delegator]; // Validation uint256 tokensToWithdraw = getWithdraweableDelegatedTokens(delegation); require(tokensToWithdraw > 0, "!tokens"); // Reset lock delegation.tokensLocked = 0; delegation.tokensLockedUntil = 0; emit StakeDelegatedWithdrawn(_indexer, _delegator, tokensToWithdraw); // -- Interactions -- if (_delegateToIndexer != address(0)) { // Re-delegate tokens to a new indexer _delegate(_delegator, _delegateToIndexer, tokensToWithdraw); } else { // Return tokens to the delegator TokenUtils.pushTokens(graphToken(), _delegator, tokensToWithdraw); } return tokensToWithdraw; } /** * @dev Collect the delegation rewards for query fees. * This function will assign the collected fees to the delegation pool. * @param _indexer Indexer to which the tokens to distribute are related * @param _tokens Total tokens received used to calculate the amount of fees to collect * @return Amount of delegation rewards */ function _collectDelegationQueryRewards(address _indexer, uint256 _tokens) private returns (uint256) { uint256 delegationRewards = 0; DelegationPool storage pool = delegationPools[_indexer]; if (pool.tokens > 0 && pool.queryFeeCut < MAX_PPM) { uint256 indexerCut = uint256(pool.queryFeeCut).mul(_tokens).div(MAX_PPM); delegationRewards = _tokens.sub(indexerCut); pool.tokens = pool.tokens.add(delegationRewards); } return delegationRewards; } /** * @dev Collect the delegation rewards for indexing. * This function will assign the collected fees to the delegation pool. * @param _indexer Indexer to which the tokens to distribute are related * @param _tokens Total tokens received used to calculate the amount of fees to collect * @return Amount of delegation rewards */ function _collectDelegationIndexingRewards(address _indexer, uint256 _tokens) private returns (uint256) { uint256 delegationRewards = 0; DelegationPool storage pool = delegationPools[_indexer]; if (pool.tokens > 0 && pool.indexingRewardCut < MAX_PPM) { uint256 indexerCut = uint256(pool.indexingRewardCut).mul(_tokens).div(MAX_PPM); delegationRewards = _tokens.sub(indexerCut); pool.tokens = pool.tokens.add(delegationRewards); } return delegationRewards; } /** * @dev Collect the curation fees for a subgraph deployment from an amount of tokens. * This function transfer curation fees to the Curation contract by calling Curation.collect * @param _graphToken Token to collect * @param _subgraphDeploymentID Subgraph deployment to which the curation fees are related * @param _tokens Total tokens received used to calculate the amount of fees to collect * @param _curationPercentage Percentage of tokens to collect as fees * @return Amount of curation fees */ function _collectCurationFees( IGraphToken _graphToken, bytes32 _subgraphDeploymentID, uint256 _tokens, uint256 _curationPercentage ) private returns (uint256) { if (_tokens == 0) { return 0; } ICuration curation = curation(); bool isCurationEnabled = _curationPercentage > 0 && address(curation) != address(0); if (isCurationEnabled && curation.isCurated(_subgraphDeploymentID)) { uint256 curationFees = uint256(_curationPercentage).mul(_tokens).div(MAX_PPM); if (curationFees > 0) { // Transfer and call collect() // This function transfer tokens to a trusted protocol contracts // Then we call collect() to do the transfer bookeeping TokenUtils.pushTokens(_graphToken, address(curation), curationFees); curation.collect(_subgraphDeploymentID, curationFees); } return curationFees; } return 0; } /** * @dev Collect tax to burn for an amount of tokens. * @param _graphToken Token to burn * @param _tokens Total tokens received used to calculate the amount of tax to collect * @param _percentage Percentage of tokens to burn as tax * @return Amount of tax charged */ function _collectTax( IGraphToken _graphToken, uint256 _tokens, uint256 _percentage ) private returns (uint256) { uint256 tax = uint256(_percentage).mul(_tokens).div(MAX_PPM); TokenUtils.burnTokens(_graphToken, tax); // Burn tax if any return tax; } /** * @dev Return the current state of an allocation * @param _allocationID Allocation identifier * @return AllocationState */ function _getAllocationState(address _allocationID) private view returns (AllocationState) { Allocation storage alloc = allocations[_allocationID]; if (alloc.indexer == address(0)) { return AllocationState.Null; } if (alloc.createdAtEpoch == 0) { return AllocationState.Claimed; } uint256 closedAtEpoch = alloc.closedAtEpoch; if (closedAtEpoch == 0) { return AllocationState.Active; } uint256 epochs = epochManager().epochsSince(closedAtEpoch); if (epochs >= channelDisputeEpochs) { return AllocationState.Finalized; } return AllocationState.Closed; } /** * @dev Get the effective stake allocation considering epochs from allocation to closing. * @param _maxAllocationEpochs Max amount of epochs to cap the allocated stake * @param _tokens Amount of tokens allocated * @param _numEpochs Number of epochs that passed from allocation to closing * @return Effective allocated tokens across epochs */ function _getEffectiveAllocation( uint256 _maxAllocationEpochs, uint256 _tokens, uint256 _numEpochs ) private pure returns (uint256) { bool shouldCap = _maxAllocationEpochs > 0 && _numEpochs > _maxAllocationEpochs; return _tokens.mul((shouldCap) ? _maxAllocationEpochs : _numEpochs); } /** * @dev Triggers an update of rewards due to a change in allocations. * @param _subgraphDeploymentID Subgraph deployment updated */ function _updateRewards(bytes32 _subgraphDeploymentID) private returns (uint256) { IRewardsManager rewardsManager = rewardsManager(); if (address(rewardsManager) == address(0)) { return 0; } return rewardsManager.onSubgraphAllocationUpdate(_subgraphDeploymentID); } /** * @dev Assign rewards for the closed allocation to indexer and delegators. * @param _allocationID Allocation */ function _distributeRewards(address _allocationID, address _indexer) private { IRewardsManager rewardsManager = rewardsManager(); if (address(rewardsManager) == address(0)) { return; } // Automatically triggers update of rewards snapshot as allocation will change // after this call. Take rewards mint tokens for the Staking contract to distribute // between indexer and delegators uint256 totalRewards = rewardsManager.takeRewards(_allocationID); if (totalRewards == 0) { return; } // Calculate delegation rewards and add them to the delegation pool uint256 delegationRewards = _collectDelegationIndexingRewards(_indexer, totalRewards); uint256 indexerRewards = totalRewards.sub(delegationRewards); // Send the indexer rewards _sendRewards( graphToken(), indexerRewards, _indexer, rewardsDestination[_indexer] == address(0) ); } /** * @dev Send rewards to the appropiate destination. * @param _graphToken Graph token * @param _amount Number of rewards tokens * @param _beneficiary Address of the beneficiary of rewards * @param _restake Whether to restake or not */ function _sendRewards( IGraphToken _graphToken, uint256 _amount, address _beneficiary, bool _restake ) private { if (_amount == 0) return; if (_restake) { // Restake to place fees into the indexer stake _stake(_beneficiary, _amount); } else { // Transfer funds to the beneficiary's designated rewards destination if set address destination = rewardsDestination[_beneficiary]; TokenUtils.pushTokens( _graphToken, destination == address(0) ? _beneficiary : destination, _amount ); } } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { // Check the signature length if (signature.length != 65) { revert("ECDSA: invalid signature length"); } // Divide the signature in r, s and v variables bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. // solhint-disable-next-line no-inline-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return recover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover-bytes32-bytes-} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. require(uint256(s) <= 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, "ECDSA: invalid signature 's' value"); require(v == 27 || v == 28, "ECDSA: invalid signature 'v' value"); // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); require(signer != address(0), "ECDSA: invalid signature"); return signer; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * replicates the behavior of the * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`] * JSON-RPC method. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\\x19Ethereum Signed Message:\ 32", hash)); } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "./IMulticall.sol"; // Inspired by https://github.com/Uniswap/uniswap-v3-periphery/blob/main/contracts/base/Multicall.sol // Note: Removed payable from the multicall /** * @title Multicall * @notice Enables calling multiple methods in a single call to the contract */ abstract contract Multicall is IMulticall { /// @inheritdoc IMulticall function multicall(bytes[] calldata data) external override returns (bytes[] memory results) { results = new bytes[](data.length); for (uint256 i = 0; i < data.length; i++) { (bool success, bytes memory result) = address(this).delegatecall(data[i]); if (!success) { // Next 5 lines from https://ethereum.stackexchange.com/a/83577 if (result.length < 68) revert(); assembly { result := add(result, 0x04) } revert(abi.decode(result, (string))); } results[i] = result; } } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import "./IGraphProxy.sol"; /** * @title Graph Upgradeable * @dev This contract is intended to be inherited from upgradeable contracts. */ contract GraphUpgradeable { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Check if the caller is the proxy admin. */ modifier onlyProxyAdmin(IGraphProxy _proxy) { require(msg.sender == _proxy.admin(), "Caller must be the proxy admin"); _; } /** * @dev Check if the caller is the implementation. */ modifier onlyImpl() { require(msg.sender == _implementation(), "Caller must be the implementation"); _; } /** * @dev Returns the current implementation. * @return impl Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Accept to be an implementation of proxy. */ function acceptProxy(IGraphProxy _proxy) external onlyProxyAdmin(_proxy) { _proxy.acceptUpgrade(); } /** * @dev Accept to be an implementation of proxy and then call a function from the new * implementation as specified by `_data`, which should be an encoded function call. This is * useful to initialize new storage variables in the proxied contract. */ function acceptProxyAndCall(IGraphProxy _proxy, bytes calldata _data) external onlyProxyAdmin(_proxy) { _proxy.acceptUpgradeAndCall(_data); } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import "../token/IGraphToken.sol"; library TokenUtils { /** * @dev Pull tokens from an address to this contract. * @param _graphToken Token to transfer * @param _from Address sending the tokens * @param _amount Amount of tokens to transfer */ function pullTokens( IGraphToken _graphToken, address _from, uint256 _amount ) internal { if (_amount > 0) { require(_graphToken.transferFrom(_from, address(this), _amount), "!transfer"); } } /** * @dev Push tokens from this contract to a receiving address. * @param _graphToken Token to transfer * @param _to Address receiving the tokens * @param _amount Amount of tokens to transfer */ function pushTokens( IGraphToken _graphToken, address _to, uint256 _amount ) internal { if (_amount > 0) { require(_graphToken.transfer(_to, _amount), "!transfer"); } } /** * @dev Burn tokens held by this contract. * @param _graphToken Token to burn * @param _amount Amount of tokens to burn */ function burnTokens(IGraphToken _graphToken, uint256 _amount) internal { if (_amount > 0) { _graphToken.burn(_amount); } } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.6.12 <0.8.0; pragma abicoder v2; import "./IStakingData.sol"; interface IStaking is IStakingData { // -- Allocation Data -- /** * @dev Possible states an allocation can be * States: * - Null = indexer == address(0) * - Active = not Null && tokens > 0 * - Closed = Active && closedAtEpoch != 0 * - Finalized = Closed && closedAtEpoch + channelDisputeEpochs > now() * - Claimed = not Null && tokens == 0 */ enum AllocationState { Null, Active, Closed, Finalized, Claimed } // -- Configuration -- function setMinimumIndexerStake(uint256 _minimumIndexerStake) external; function setThawingPeriod(uint32 _thawingPeriod) external; function setCurationPercentage(uint32 _percentage) external; function setProtocolPercentage(uint32 _percentage) external; function setChannelDisputeEpochs(uint32 _channelDisputeEpochs) external; function setMaxAllocationEpochs(uint32 _maxAllocationEpochs) external; function setRebateRatio(uint32 _alphaNumerator, uint32 _alphaDenominator) external; function setDelegationRatio(uint32 _delegationRatio) external; function setDelegationParameters( uint32 _indexingRewardCut, uint32 _queryFeeCut, uint32 _cooldownBlocks ) external; function setDelegationParametersCooldown(uint32 _blocks) external; function setDelegationUnbondingPeriod(uint32 _delegationUnbondingPeriod) external; function setDelegationTaxPercentage(uint32 _percentage) external; function setSlasher(address _slasher, bool _allowed) external; function setAssetHolder(address _assetHolder, bool _allowed) external; // -- Operation -- function setOperator(address _operator, bool _allowed) external; function isOperator(address _operator, address _indexer) external view returns (bool); // -- Staking -- function stake(uint256 _tokens) external; function stakeTo(address _indexer, uint256 _tokens) external; function unstake(uint256 _tokens) external; function slash( address _indexer, uint256 _tokens, uint256 _reward, address _beneficiary ) external; function withdraw() external; function setRewardsDestination(address _destination) external; // -- Delegation -- function delegate(address _indexer, uint256 _tokens) external returns (uint256); function undelegate(address _indexer, uint256 _shares) external returns (uint256); function withdrawDelegated(address _indexer, address _newIndexer) external returns (uint256); // -- Channel management and allocations -- function allocate( bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external; function allocateFrom( address _indexer, bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external; function closeAllocation(address _allocationID, bytes32 _poi) external; function closeAllocationMany(CloseAllocationRequest[] calldata _requests) external; function closeAndAllocate( address _oldAllocationID, bytes32 _poi, address _indexer, bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external; function collect(uint256 _tokens, address _allocationID) external; function claim(address _allocationID, bool _restake) external; function claimMany(address[] calldata _allocationID, bool _restake) external; // -- Getters and calculations -- function hasStake(address _indexer) external view returns (bool); function getIndexerStakedTokens(address _indexer) external view returns (uint256); function getIndexerCapacity(address _indexer) external view returns (uint256); function getAllocation(address _allocationID) external view returns (Allocation memory); function getAllocationState(address _allocationID) external view returns (AllocationState); function isAllocation(address _allocationID) external view returns (bool); function getSubgraphAllocatedTokens(bytes32 _subgraphDeploymentID) external view returns (uint256); function getDelegation(address _indexer, address _delegator) external view returns (Delegation memory); function isDelegator(address _indexer, address _delegator) external view returns (bool); } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import "../governance/Managed.sol"; import "./IStakingData.sol"; import "./libs/Rebates.sol"; import "./libs/Stakes.sol"; contract StakingV1Storage is Managed { // -- Staking -- // Minimum amount of tokens an indexer needs to stake uint256 public minimumIndexerStake; // Time in blocks to unstake uint32 public thawingPeriod; // in blocks // Percentage of fees going to curators // Parts per million. (Allows for 4 decimal points, 999,999 = 99.9999%) uint32 public curationPercentage; // Percentage of fees burned as protocol fee // Parts per million. (Allows for 4 decimal points, 999,999 = 99.9999%) uint32 public protocolPercentage; // Period for allocation to be finalized uint32 public channelDisputeEpochs; // Maximum allocation time uint32 public maxAllocationEpochs; // Rebate ratio uint32 public alphaNumerator; uint32 public alphaDenominator; // Indexer stakes : indexer => Stake mapping(address => Stakes.Indexer) public stakes; // Allocations : allocationID => Allocation mapping(address => IStakingData.Allocation) public allocations; // Subgraph Allocations: subgraphDeploymentID => tokens mapping(bytes32 => uint256) public subgraphAllocations; // Rebate pools : epoch => Pool mapping(uint256 => Rebates.Pool) public rebates; // -- Slashing -- // List of addresses allowed to slash stakes mapping(address => bool) public slashers; // -- Delegation -- // Set the delegation capacity multiplier defined by the delegation ratio // If delegation ratio is 100, and an Indexer has staked 5 GRT, // then they can use up to 500 GRT from the delegated stake uint32 public delegationRatio; // Time in blocks an indexer needs to wait to change delegation parameters uint32 public delegationParametersCooldown; // Time in epochs a delegator needs to wait to withdraw delegated stake uint32 public delegationUnbondingPeriod; // in epochs // Percentage of tokens to tax a delegation deposit // Parts per million. (Allows for 4 decimal points, 999,999 = 99.9999%) uint32 public delegationTaxPercentage; // Delegation pools : indexer => DelegationPool mapping(address => IStakingData.DelegationPool) public delegationPools; // -- Operators -- // Operator auth : indexer => operator mapping(address => mapping(address => bool)) public operatorAuth; // -- Asset Holders -- // Allowed AssetHolders: assetHolder => is allowed mapping(address => bool) public assetHolders; } contract StakingV2Storage is StakingV1Storage { // Destination of accrued rewards : beneficiary => rewards destination mapping(address => address) public rewardsDestination; } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import "@openzeppelin/contracts/math/SafeMath.sol"; /** * @title MathUtils Library * @notice A collection of functions to perform math operations */ library MathUtils { using SafeMath for uint256; /** * @dev Calculates the weighted average of two values pondering each of these * values based on configured weights. The contribution of each value N is * weightN/(weightA + weightB). * @param valueA The amount for value A * @param weightA The weight to use for value A * @param valueB The amount for value B * @param weightB The weight to use for value B */ function weightedAverage( uint256 valueA, uint256 weightA, uint256 valueB, uint256 weightB ) internal pure returns (uint256) { return valueA.mul(weightA).add(valueB.mul(weightB)).div(weightA.add(weightB)); } /** * @dev Returns the minimum of two numbers. */ function min(uint256 x, uint256 y) internal pure returns (uint256) { return x <= y ? x : y; } /** * @dev Returns the difference between two numbers or zero if negative. */ function diffOrZero(uint256 x, uint256 y) internal pure returns (uint256) { return (x > y) ? x.sub(y) : 0; } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "./Cobbs.sol"; /** * @title A collection of data structures and functions to manage Rebates * Used for low-level state changes, require() conditions should be evaluated * at the caller function scope. */ library Rebates { using SafeMath for uint256; using Rebates for Rebates.Pool; // Tracks stats for allocations closed on a particular epoch for claiming // The pool also keeps tracks of total query fees collected and stake used // Only one rebate pool exists per epoch struct Pool { uint256 fees; // total query fees in the rebate pool uint256 effectiveAllocatedStake; // total effective allocation of stake uint256 claimedRewards; // total claimed rewards from the rebate pool uint32 unclaimedAllocationsCount; // amount of unclaimed allocations uint32 alphaNumerator; // numerator of `alpha` in the cobb-douglas function uint32 alphaDenominator; // denominator of `alpha` in the cobb-douglas function } /** * @dev Init the rebate pool with the rebate ratio. * @param _alphaNumerator Numerator of `alpha` in the cobb-douglas function * @param _alphaDenominator Denominator of `alpha` in the cobb-douglas function */ function init( Rebates.Pool storage pool, uint32 _alphaNumerator, uint32 _alphaDenominator ) internal { pool.alphaNumerator = _alphaNumerator; pool.alphaDenominator = _alphaDenominator; } /** * @dev Return true if the rebate pool was already initialized. */ function exists(Rebates.Pool storage pool) internal view returns (bool) { return pool.effectiveAllocatedStake > 0; } /** * @dev Return the amount of unclaimed fees. */ function unclaimedFees(Rebates.Pool storage pool) internal view returns (uint256) { return pool.fees.sub(pool.claimedRewards); } /** * @dev Deposit tokens into the rebate pool. * @param _indexerFees Amount of fees collected in tokens * @param _indexerEffectiveAllocatedStake Effective stake allocated by indexer for a period of epochs */ function addToPool( Rebates.Pool storage pool, uint256 _indexerFees, uint256 _indexerEffectiveAllocatedStake ) internal { pool.fees = pool.fees.add(_indexerFees); pool.effectiveAllocatedStake = pool.effectiveAllocatedStake.add( _indexerEffectiveAllocatedStake ); pool.unclaimedAllocationsCount += 1; } /** * @dev Redeem tokens from the rebate pool. * @param _indexerFees Amount of fees collected in tokens * @param _indexerEffectiveAllocatedStake Effective stake allocated by indexer for a period of epochs * @return Amount of reward tokens according to Cobb-Douglas rebate formula */ function redeem( Rebates.Pool storage pool, uint256 _indexerFees, uint256 _indexerEffectiveAllocatedStake ) internal returns (uint256) { uint256 rebateReward = 0; // Calculate the rebate rewards for the indexer if (pool.fees > 0 && pool.effectiveAllocatedStake > 0) { rebateReward = LibCobbDouglas.cobbDouglas( pool.fees, // totalRewards _indexerFees, pool.fees, _indexerEffectiveAllocatedStake, pool.effectiveAllocatedStake, pool.alphaNumerator, pool.alphaDenominator ); // Under NO circumstance we will reward more than total fees in the pool uint256 _unclaimedFees = pool.unclaimedFees(); if (rebateReward > _unclaimedFees) { rebateReward = _unclaimedFees; } } // Update pool state pool.unclaimedAllocationsCount -= 1; pool.claimedRewards = pool.claimedRewards.add(rebateReward); return rebateReward; } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; import "@openzeppelin/contracts/math/SafeMath.sol"; import "./MathUtils.sol"; /** * @title A collection of data structures and functions to manage the Indexer Stake state. * Used for low-level state changes, require() conditions should be evaluated * at the caller function scope. */ library Stakes { using SafeMath for uint256; using Stakes for Stakes.Indexer; struct Indexer { uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer) uint256 tokensAllocated; // Tokens used in allocations uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn } /** * @dev Deposit tokens to the indexer stake. * @param stake Stake data * @param _tokens Amount of tokens to deposit */ function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal { stake.tokensStaked = stake.tokensStaked.add(_tokens); } /** * @dev Release tokens from the indexer stake. * @param stake Stake data * @param _tokens Amount of tokens to release */ function release(Stakes.Indexer storage stake, uint256 _tokens) internal { stake.tokensStaked = stake.tokensStaked.sub(_tokens); } /** * @dev Allocate tokens from the main stack to a SubgraphDeployment. * @param stake Stake data * @param _tokens Amount of tokens to allocate */ function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal { stake.tokensAllocated = stake.tokensAllocated.add(_tokens); } /** * @dev Unallocate tokens from a SubgraphDeployment back to the main stack. * @param stake Stake data * @param _tokens Amount of tokens to unallocate */ function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal { stake.tokensAllocated = stake.tokensAllocated.sub(_tokens); } /** * @dev Lock tokens until a thawing period pass. * @param stake Stake data * @param _tokens Amount of tokens to unstake * @param _period Period in blocks that need to pass before withdrawal */ function lockTokens( Stakes.Indexer storage stake, uint256 _tokens, uint256 _period ) internal { // Take into account period averaging for multiple unstake requests uint256 lockingPeriod = _period; if (stake.tokensLocked > 0) { lockingPeriod = MathUtils.weightedAverage( MathUtils.diffOrZero(stake.tokensLockedUntil, block.number), // Remaining thawing period stake.tokensLocked, // Weighted by remaining unstaked tokens _period, // Thawing period _tokens // Weighted by new tokens to unstake ); } // Update balances stake.tokensLocked = stake.tokensLocked.add(_tokens); stake.tokensLockedUntil = block.number.add(lockingPeriod); } /** * @dev Unlock tokens. * @param stake Stake data * @param _tokens Amount of tokens to unkock */ function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal { stake.tokensLocked = stake.tokensLocked.sub(_tokens); if (stake.tokensLocked == 0) { stake.tokensLockedUntil = 0; } } /** * @dev Take all tokens out from the locked stake for withdrawal. * @param stake Stake data * @return Amount of tokens being withdrawn */ function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) { // Calculate tokens that can be released uint256 tokensToWithdraw = stake.tokensWithdrawable(); if (tokensToWithdraw > 0) { // Reset locked tokens stake.unlockTokens(tokensToWithdraw); // Decrease indexer stake stake.release(tokensToWithdraw); } return tokensToWithdraw; } /** * @dev Return the amount of tokens used in allocations and locked for withdrawal. * @param stake Stake data * @return Token amount */ function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) { return stake.tokensAllocated.add(stake.tokensLocked); } /** * @dev Return the amount of tokens staked not considering the ones that are already going * through the thawing period or are ready for withdrawal. We call it secure stake because * it is not subject to change by a withdraw call from the indexer. * @param stake Stake data * @return Token amount */ function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) { return stake.tokensStaked.sub(stake.tokensLocked); } /** * @dev Tokens free balance on the indexer stake that can be used for any purpose. * Any token that is allocated cannot be used as well as tokens that are going through the * thawing period or are withdrawable * Calc: tokensStaked - tokensAllocated - tokensLocked * @param stake Stake data * @return Token amount */ function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) { return stake.tokensAvailableWithDelegation(0); } /** * @dev Tokens free balance on the indexer stake that can be used for allocations. * This function accepts a parameter for extra delegated capacity that takes into * account delegated tokens * @param stake Stake data * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability * @return Token amount */ function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity) internal pure returns (uint256) { uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity); uint256 _tokensUsed = stake.tokensUsed(); // If more tokens are used than the current capacity, the indexer is overallocated. // This means the indexer doesn't have available capacity to create new allocations. // We can reach this state when the indexer has funds allocated and then any // of these conditions happen: // - The delegationCapacity ratio is reduced. // - The indexer stake is slashed. // - A delegator removes enough stake. if (_tokensUsed > tokensCapacity) { // Indexer stake is over allocated: return 0 to avoid stake to be used until // the overallocation is restored by staking more tokens, unallocating tokens // or using more delegated funds return 0; } return tokensCapacity.sub(_tokensUsed); } /** * @dev Tokens available for withdrawal after thawing period. * @param stake Stake data * @return Token amount */ function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) { // No tokens to withdraw before locking period if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) { return 0; } return stake.tokensLocked; } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; pragma abicoder v2; /** * @title Multicall interface * @notice Enables calling multiple methods in a single call to the contract */ interface IMulticall { /** * @notice Call multiple functions in the current contract and return the data from all of them if they all succeed * @param data The encoded function data for each of the calls to make to this contract * @return results The results from each of the calls passed in via data */ function multicall(bytes[] calldata data) external returns (bytes[] memory results); } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; interface IGraphProxy { function admin() external returns (address); function setAdmin(address _newAdmin) external; function implementation() external returns (address); function pendingImplementation() external returns (address); function upgradeTo(address _newImplementation) external; function acceptUpgrade() external; function acceptUpgradeAndCall(bytes calldata data) external; } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IGraphToken is IERC20 { // -- Mint and Burn -- function burn(uint256 amount) external; function mint(address _to, uint256 _amount) external; // -- Mint Admin -- function addMinter(address _account) external; function removeMinter(address _account) external; function renounceMinter() external; function isMinter(address _account) external view returns (bool); // -- Permit -- function permit( address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s ) external; } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.6.12 <0.8.0; interface IStakingData { /** * @dev Allocate GRT tokens for the purpose of serving queries of a subgraph deployment * An allocation is created in the allocate() function and consumed in claim() */ struct Allocation { address indexer; bytes32 subgraphDeploymentID; uint256 tokens; // Tokens allocated to a SubgraphDeployment uint256 createdAtEpoch; // Epoch when it was created uint256 closedAtEpoch; // Epoch when it was closed uint256 collectedFees; // Collected fees for the allocation uint256 effectiveAllocation; // Effective allocation when closed uint256 accRewardsPerAllocatedToken; // Snapshot used for reward calc } /** * @dev Represents a request to close an allocation with a specific proof of indexing. * This is passed when calling closeAllocationMany to define the closing parameters for * each allocation. */ struct CloseAllocationRequest { address allocationID; bytes32 poi; } // -- Delegation Data -- /** * @dev Delegation pool information. One per indexer. */ struct DelegationPool { uint32 cooldownBlocks; // Blocks to wait before updating parameters uint32 indexingRewardCut; // in PPM uint32 queryFeeCut; // in PPM uint256 updatedAtBlock; // Block when the pool was last updated uint256 tokens; // Total tokens as pool reserves uint256 shares; // Total shares minted in the pool mapping(address => Delegation) delegators; // Mapping of delegator => Delegation } /** * @dev Individual delegation data of a delegator in a pool. */ struct Delegation { uint256 shares; // Shares owned by a delegator in the pool uint256 tokensLocked; // Tokens locked for undelegation uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import "./IController.sol"; import "../curation/ICuration.sol"; import "../epochs/IEpochManager.sol"; import "../rewards/IRewardsManager.sol"; import "../staking/IStaking.sol"; import "../token/IGraphToken.sol"; /** * @title Graph Managed contract * @dev The Managed contract provides an interface to interact with the Controller. * It also provides local caching for contract addresses. This mechanism relies on calling the * public `syncAllContracts()` function whenever a contract changes in the controller. * * Inspired by Livepeer: * https://github.com/livepeer/protocol/blob/streamflow/contracts/Controller.sol */ contract Managed { // -- State -- // Controller that contract is registered with IController public controller; mapping(bytes32 => address) private addressCache; uint256[10] private __gap; // -- Events -- event ParameterUpdated(string param); event SetController(address controller); /** * @dev Emitted when contract with `nameHash` is synced to `contractAddress`. */ event ContractSynced(bytes32 indexed nameHash, address contractAddress); // -- Modifiers -- function _notPartialPaused() internal view { require(!controller.paused(), "Paused"); require(!controller.partialPaused(), "Partial-paused"); } function _notPaused() internal view { require(!controller.paused(), "Paused"); } function _onlyGovernor() internal view { require(msg.sender == controller.getGovernor(), "Caller must be Controller governor"); } function _onlyController() internal view { require(msg.sender == address(controller), "Caller must be Controller"); } modifier notPartialPaused() { _notPartialPaused(); _; } modifier notPaused() { _notPaused(); _; } // Check if sender is controller. modifier onlyController() { _onlyController(); _; } // Check if sender is the governor. modifier onlyGovernor() { _onlyGovernor(); _; } // -- Functions -- /** * @dev Initialize the controller. */ function _initialize(address _controller) internal { _setController(_controller); } /** * @notice Set Controller. Only callable by current controller. * @param _controller Controller contract address */ function setController(address _controller) external onlyController { _setController(_controller); } /** * @dev Set controller. * @param _controller Controller contract address */ function _setController(address _controller) internal { require(_controller != address(0), "Controller must be set"); controller = IController(_controller); emit SetController(_controller); } /** * @dev Return Curation interface. * @return Curation contract registered with Controller */ function curation() internal view returns (ICuration) { return ICuration(_resolveContract(keccak256("Curation"))); } /** * @dev Return EpochManager interface. * @return Epoch manager contract registered with Controller */ function epochManager() internal view returns (IEpochManager) { return IEpochManager(_resolveContract(keccak256("EpochManager"))); } /** * @dev Return RewardsManager interface. * @return Rewards manager contract registered with Controller */ function rewardsManager() internal view returns (IRewardsManager) { return IRewardsManager(_resolveContract(keccak256("RewardsManager"))); } /** * @dev Return Staking interface. * @return Staking contract registered with Controller */ function staking() internal view returns (IStaking) { return IStaking(_resolveContract(keccak256("Staking"))); } /** * @dev Return GraphToken interface. * @return Graph token contract registered with Controller */ function graphToken() internal view returns (IGraphToken) { return IGraphToken(_resolveContract(keccak256("GraphToken"))); } /** * @dev Resolve a contract address from the cache or the Controller if not found. * @return Address of the contract */ function _resolveContract(bytes32 _nameHash) internal view returns (address) { address contractAddress = addressCache[_nameHash]; if (contractAddress == address(0)) { contractAddress = controller.getContractProxy(_nameHash); } return contractAddress; } /** * @dev Cache a contract address from the Controller registry. * @param _name Name of the contract to sync into the cache */ function _syncContract(string memory _name) internal { bytes32 nameHash = keccak256(abi.encodePacked(_name)); address contractAddress = controller.getContractProxy(nameHash); if (addressCache[nameHash] != contractAddress) { addressCache[nameHash] = contractAddress; emit ContractSynced(nameHash, contractAddress); } } /** * @dev Sync protocol contract addresses from the Controller registry. * This function will cache all the contracts using the latest addresses * Anyone can call the function whenever a Proxy contract change in the * controller to ensure the protocol is using the latest version */ function syncAllContracts() external { _syncContract("Curation"); _syncContract("EpochManager"); _syncContract("RewardsManager"); _syncContract("Staking"); _syncContract("GraphToken"); } } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.6.12 <0.8.0; interface IController { function getGovernor() external view returns (address); // -- Registry -- function setContractProxy(bytes32 _id, address _contractAddress) external; function unsetContractProxy(bytes32 _id) external; function updateController(bytes32 _id, address _controller) external; function getContractProxy(bytes32 _id) external view returns (address); // -- Pausing -- function setPartialPaused(bool _partialPaused) external; function setPaused(bool _paused) external; function setPauseGuardian(address _newPauseGuardian) external; function paused() external view returns (bool); function partialPaused() external view returns (bool); } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import "./IGraphCurationToken.sol"; interface ICuration { // -- Configuration -- function setDefaultReserveRatio(uint32 _defaultReserveRatio) external; function setMinimumCurationDeposit(uint256 _minimumCurationDeposit) external; function setCurationTaxPercentage(uint32 _percentage) external; function setCurationTokenMaster(address _curationTokenMaster) external; // -- Curation -- function mint( bytes32 _subgraphDeploymentID, uint256 _tokensIn, uint256 _signalOutMin ) external returns (uint256, uint256); function burn( bytes32 _subgraphDeploymentID, uint256 _signalIn, uint256 _tokensOutMin ) external returns (uint256); function collect(bytes32 _subgraphDeploymentID, uint256 _tokens) external; // -- Getters -- function isCurated(bytes32 _subgraphDeploymentID) external view returns (bool); function getCuratorSignal(address _curator, bytes32 _subgraphDeploymentID) external view returns (uint256); function getCurationPoolSignal(bytes32 _subgraphDeploymentID) external view returns (uint256); function getCurationPoolTokens(bytes32 _subgraphDeploymentID) external view returns (uint256); function tokensToSignal(bytes32 _subgraphDeploymentID, uint256 _tokensIn) external view returns (uint256, uint256); function signalToTokens(bytes32 _subgraphDeploymentID, uint256 _signalIn) external view returns (uint256); function curationTaxPercentage() external view returns (uint32); } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; interface IEpochManager { // -- Configuration -- function setEpochLength(uint256 _epochLength) external; // -- Epochs function runEpoch() external; // -- Getters -- function isCurrentEpochRun() external view returns (bool); function blockNum() external view returns (uint256); function blockHash(uint256 _block) external view returns (bytes32); function currentEpoch() external view returns (uint256); function currentEpochBlock() external view returns (uint256); function currentEpochBlockSinceStart() external view returns (uint256); function epochsSince(uint256 _epoch) external view returns (uint256); function epochsSinceUpdate() external view returns (uint256); } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; interface IRewardsManager { /** * @dev Stores accumulated rewards and snapshots related to a particular SubgraphDeployment. */ struct Subgraph { uint256 accRewardsForSubgraph; uint256 accRewardsForSubgraphSnapshot; uint256 accRewardsPerSignalSnapshot; uint256 accRewardsPerAllocatedToken; } // -- Config -- function setIssuanceRate(uint256 _issuanceRate) external; function setMinimumSubgraphSignal(uint256 _minimumSubgraphSignal) external; // -- Denylist -- function setSubgraphAvailabilityOracle(address _subgraphAvailabilityOracle) external; function setDenied(bytes32 _subgraphDeploymentID, bool _deny) external; function setDeniedMany(bytes32[] calldata _subgraphDeploymentID, bool[] calldata _deny) external; function isDenied(bytes32 _subgraphDeploymentID) external view returns (bool); // -- Getters -- function getNewRewardsPerSignal() external view returns (uint256); function getAccRewardsPerSignal() external view returns (uint256); function getAccRewardsForSubgraph(bytes32 _subgraphDeploymentID) external view returns (uint256); function getAccRewardsPerAllocatedToken(bytes32 _subgraphDeploymentID) external view returns (uint256, uint256); function getRewards(address _allocationID) external view returns (uint256); // -- Updates -- function updateAccRewardsPerSignal() external returns (uint256); function takeRewards(address _allocationID) external returns (uint256); // -- Hooks -- function onSubgraphSignalUpdate(bytes32 _subgraphDeploymentID) external returns (uint256); function onSubgraphAllocationUpdate(bytes32 _subgraphDeploymentID) external returns (uint256); } // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.7.6; import "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; interface IGraphCurationToken is IERC20Upgradeable { function initialize(address _owner) external; function burnFrom(address _account, uint256 _amount) external; function mint(address _to, uint256 _amount) external; } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } } /* Copyright 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.7.6; pragma abicoder v2; import "./LibFixedMath.sol"; library LibCobbDouglas { /// @dev The cobb-douglas function used to compute fee-based rewards for /// staking pools in a given epoch. This function does not perform /// bounds checking on the inputs, but the following conditions /// need to be true: /// 0 <= fees / totalFees <= 1 /// 0 <= stake / totalStake <= 1 /// 0 <= alphaNumerator / alphaDenominator <= 1 /// @param totalRewards collected over an epoch. /// @param fees Fees attributed to the the staking pool. /// @param totalFees Total fees collected across all pools that earned rewards. /// @param stake Stake attributed to the staking pool. /// @param totalStake Total stake across all pools that earned rewards. /// @param alphaNumerator Numerator of `alpha` in the cobb-douglas function. /// @param alphaDenominator Denominator of `alpha` in the cobb-douglas /// function. /// @return rewards Rewards owed to the staking pool. function cobbDouglas( uint256 totalRewards, uint256 fees, uint256 totalFees, uint256 stake, uint256 totalStake, uint32 alphaNumerator, uint32 alphaDenominator ) public pure returns (uint256 rewards) { int256 feeRatio = LibFixedMath.toFixed(fees, totalFees); int256 stakeRatio = LibFixedMath.toFixed(stake, totalStake); if (feeRatio == 0 || stakeRatio == 0) { return rewards = 0; } // The cobb-doublas function has the form: // `totalRewards * feeRatio ^ alpha * stakeRatio ^ (1-alpha)` // This is equivalent to: // `totalRewards * stakeRatio * e^(alpha * (ln(feeRatio / stakeRatio)))` // However, because `ln(x)` has the domain of `0 < x < 1` // and `exp(x)` has the domain of `x < 0`, // and fixed-point math easily overflows with multiplication, // we will choose the following if `stakeRatio > feeRatio`: // `totalRewards * stakeRatio / e^(alpha * (ln(stakeRatio / feeRatio)))` // Compute // `e^(alpha * ln(feeRatio/stakeRatio))` if feeRatio <= stakeRatio // or // `e^(alpa * ln(stakeRatio/feeRatio))` if feeRatio > stakeRatio int256 n = feeRatio <= stakeRatio ? LibFixedMath.div(feeRatio, stakeRatio) : LibFixedMath.div(stakeRatio, feeRatio); n = LibFixedMath.exp( LibFixedMath.mulDiv( LibFixedMath.ln(n), int256(alphaNumerator), int256(alphaDenominator) ) ); // Compute // `totalRewards * n` if feeRatio <= stakeRatio // or // `totalRewards / n` if stakeRatio > feeRatio // depending on the choice we made earlier. n = feeRatio <= stakeRatio ? LibFixedMath.mul(stakeRatio, n) : LibFixedMath.div(stakeRatio, n); // Multiply the above with totalRewards. rewards = LibFixedMath.uintMul(n, totalRewards); } } /* Copyright 2017 Bprotocol Foundation, 2019 ZeroEx Intl. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ // SPDX-License-Identifier: Apache-2.0 pragma solidity ^0.7.6; // solhint-disable indent /// @dev Signed, fixed-point, 127-bit precision math library. library LibFixedMath { // 1 int256 private constant FIXED_1 = int256(0x0000000000000000000000000000000080000000000000000000000000000000); // 2**255 int256 private constant MIN_FIXED_VAL = int256(0x8000000000000000000000000000000000000000000000000000000000000000); // 1^2 (in fixed-point) int256 private constant FIXED_1_SQUARED = int256(0x4000000000000000000000000000000000000000000000000000000000000000); // 1 int256 private constant LN_MAX_VAL = FIXED_1; // e ^ -63.875 int256 private constant LN_MIN_VAL = int256(0x0000000000000000000000000000000000000000000000000000000733048c5a); // 0 int256 private constant EXP_MAX_VAL = 0; // -63.875 int256 private constant EXP_MIN_VAL = -int256(0x0000000000000000000000000000001ff0000000000000000000000000000000); /// @dev Get one as a fixed-point number. function one() internal pure returns (int256 f) { f = FIXED_1; } /// @dev Returns the addition of two fixed point numbers, reverting on overflow. function add(int256 a, int256 b) internal pure returns (int256 c) { c = _add(a, b); } /// @dev Returns the addition of two fixed point numbers, reverting on overflow. function sub(int256 a, int256 b) internal pure returns (int256 c) { if (b == MIN_FIXED_VAL) { revert("out-of-bounds"); } c = _add(a, -b); } /// @dev Returns the multiplication of two fixed point numbers, reverting on overflow. function mul(int256 a, int256 b) internal pure returns (int256 c) { c = _mul(a, b) / FIXED_1; } /// @dev Returns the division of two fixed point numbers. function div(int256 a, int256 b) internal pure returns (int256 c) { c = _div(_mul(a, FIXED_1), b); } /// @dev Performs (a * n) / d, without scaling for precision. function mulDiv( int256 a, int256 n, int256 d ) internal pure returns (int256 c) { c = _div(_mul(a, n), d); } /// @dev Returns the unsigned integer result of multiplying a fixed-point /// number with an integer, reverting if the multiplication overflows. /// Negative results are clamped to zero. function uintMul(int256 f, uint256 u) internal pure returns (uint256) { if (int256(u) < int256(0)) { revert("out-of-bounds"); } int256 c = _mul(f, int256(u)); if (c <= 0) { return 0; } return uint256(uint256(c) >> 127); } /// @dev Returns the absolute value of a fixed point number. function abs(int256 f) internal pure returns (int256 c) { if (f == MIN_FIXED_VAL) { revert("out-of-bounds"); } if (f >= 0) { c = f; } else { c = -f; } } /// @dev Returns 1 / `x`, where `x` is a fixed-point number. function invert(int256 f) internal pure returns (int256 c) { c = _div(FIXED_1_SQUARED, f); } /// @dev Convert signed `n` / 1 to a fixed-point number. function toFixed(int256 n) internal pure returns (int256 f) { f = _mul(n, FIXED_1); } /// @dev Convert signed `n` / `d` to a fixed-point number. function toFixed(int256 n, int256 d) internal pure returns (int256 f) { f = _div(_mul(n, FIXED_1), d); } /// @dev Convert unsigned `n` / 1 to a fixed-point number. /// Reverts if `n` is too large to fit in a fixed-point number. function toFixed(uint256 n) internal pure returns (int256 f) { if (int256(n) < int256(0)) { revert("out-of-bounds"); } f = _mul(int256(n), FIXED_1); } /// @dev Convert unsigned `n` / `d` to a fixed-point number. /// Reverts if `n` / `d` is too large to fit in a fixed-point number. function toFixed(uint256 n, uint256 d) internal pure returns (int256 f) { if (int256(n) < int256(0)) { revert("out-of-bounds"); } if (int256(d) < int256(0)) { revert("out-of-bounds"); } f = _div(_mul(int256(n), FIXED_1), int256(d)); } /// @dev Convert a fixed-point number to an integer. function toInteger(int256 f) internal pure returns (int256 n) { return f / FIXED_1; } /// @dev Get the natural logarithm of a fixed-point number 0 < `x` <= LN_MAX_VAL function ln(int256 x) internal pure returns (int256 r) { if (x > LN_MAX_VAL) { revert("out-of-bounds"); } if (x <= 0) { revert("too-small"); } if (x == FIXED_1) { return 0; } if (x <= LN_MIN_VAL) { return EXP_MIN_VAL; } int256 y; int256 z; int256 w; // Rewrite the input as a quotient of negative natural exponents and a single residual q, such that 1 < q < 2 // For example: log(0.3) = log(e^-1 * e^-0.25 * 1.0471028872385522) // = 1 - 0.25 - log(1 + 0.0471028872385522) // e ^ -32 if (x <= int256(0x00000000000000000000000000000000000000000001c8464f76164760000000)) { r -= int256(0x0000000000000000000000000000001000000000000000000000000000000000); // - 32 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000000001c8464f76164760000000); // / e ^ -32 } // e ^ -16 if (x <= int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000)) { r -= int256(0x0000000000000000000000000000000800000000000000000000000000000000); // - 16 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000000f1aaddd7742e90000000000000); // / e ^ -16 } // e ^ -8 if (x <= int256(0x00000000000000000000000000000000000afe10820813d78000000000000000)) { r -= int256(0x0000000000000000000000000000000400000000000000000000000000000000); // - 8 x = (x * FIXED_1) / int256(0x00000000000000000000000000000000000afe10820813d78000000000000000); // / e ^ -8 } // e ^ -4 if (x <= int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000)) { r -= int256(0x0000000000000000000000000000000200000000000000000000000000000000); // - 4 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000002582ab704279ec00000000000000000); // / e ^ -4 } // e ^ -2 if (x <= int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000)) { r -= int256(0x0000000000000000000000000000000100000000000000000000000000000000); // - 2 x = (x * FIXED_1) / int256(0x000000000000000000000000000000001152aaa3bf81cc000000000000000000); // / e ^ -2 } // e ^ -1 if (x <= int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000)) { r -= int256(0x0000000000000000000000000000000080000000000000000000000000000000); // - 1 x = (x * FIXED_1) / int256(0x000000000000000000000000000000002f16ac6c59de70000000000000000000); // / e ^ -1 } // e ^ -0.5 if (x <= int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000)) { r -= int256(0x0000000000000000000000000000000040000000000000000000000000000000); // - 0.5 x = (x * FIXED_1) / int256(0x000000000000000000000000000000004da2cbf1be5828000000000000000000); // / e ^ -0.5 } // e ^ -0.25 if (x <= int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000)) { r -= int256(0x0000000000000000000000000000000020000000000000000000000000000000); // - 0.25 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000063afbe7ab2082c000000000000000000); // / e ^ -0.25 } // e ^ -0.125 if (x <= int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) { r -= int256(0x0000000000000000000000000000000010000000000000000000000000000000); // - 0.125 x = (x * FIXED_1) / int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d); // / e ^ -0.125 } // `x` is now our residual in the range of 1 <= x <= 2 (or close enough). // Add the taylor series for log(1 + z), where z = x - 1 z = y = x - FIXED_1; w = (y * y) / FIXED_1; r += (z * (0x100000000000000000000000000000000 - y)) / 0x100000000000000000000000000000000; z = (z * w) / FIXED_1; // add y^01 / 01 - y^02 / 02 r += (z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y)) / 0x200000000000000000000000000000000; z = (z * w) / FIXED_1; // add y^03 / 03 - y^04 / 04 r += (z * (0x099999999999999999999999999999999 - y)) / 0x300000000000000000000000000000000; z = (z * w) / FIXED_1; // add y^05 / 05 - y^06 / 06 r += (z * (0x092492492492492492492492492492492 - y)) / 0x400000000000000000000000000000000; z = (z * w) / FIXED_1; // add y^07 / 07 - y^08 / 08 r += (z * (0x08e38e38e38e38e38e38e38e38e38e38e - y)) / 0x500000000000000000000000000000000; z = (z * w) / FIXED_1; // add y^09 / 09 - y^10 / 10 r += (z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y)) / 0x600000000000000000000000000000000; z = (z * w) / FIXED_1; // add y^11 / 11 - y^12 / 12 r += (z * (0x089d89d89d89d89d89d89d89d89d89d89 - y)) / 0x700000000000000000000000000000000; z = (z * w) / FIXED_1; // add y^13 / 13 - y^14 / 14 r += (z * (0x088888888888888888888888888888888 - y)) / 0x800000000000000000000000000000000; // add y^15 / 15 - y^16 / 16 } /// @dev Compute the natural exponent for a fixed-point number EXP_MIN_VAL <= `x` <= 1 function exp(int256 x) internal pure returns (int256 r) { if (x < EXP_MIN_VAL) { // Saturate to zero below EXP_MIN_VAL. return 0; } if (x == 0) { return FIXED_1; } if (x > EXP_MAX_VAL) { revert("out-of-bounds"); } // Rewrite the input as a product of natural exponents and a // single residual q, where q is a number of small magnitude. // For example: e^-34.419 = e^(-32 - 2 - 0.25 - 0.125 - 0.044) // = e^-32 * e^-2 * e^-0.25 * e^-0.125 * e^-0.044 // -> q = -0.044 // Multiply with the taylor series for e^q int256 y; int256 z; // q = x % 0.125 (the residual) z = y = x % 0x0000000000000000000000000000000010000000000000000000000000000000; z = (z * y) / FIXED_1; r += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!) z = (z * y) / FIXED_1; r += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!) z = (z * y) / FIXED_1; r += z * 0x0168244fdac78000; // add y^04 * (20! / 04!) z = (z * y) / FIXED_1; r += z * 0x004807432bc18000; // add y^05 * (20! / 05!) z = (z * y) / FIXED_1; r += z * 0x000c0135dca04000; // add y^06 * (20! / 06!) z = (z * y) / FIXED_1; r += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!) z = (z * y) / FIXED_1; r += z * 0x000036e0f639b800; // add y^08 * (20! / 08!) z = (z * y) / FIXED_1; r += z * 0x00000618fee9f800; // add y^09 * (20! / 09!) z = (z * y) / FIXED_1; r += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!) z = (z * y) / FIXED_1; r += z * 0x0000000e30dce400; // add y^11 * (20! / 11!) z = (z * y) / FIXED_1; r += z * 0x000000012ebd1300; // add y^12 * (20! / 12!) z = (z * y) / FIXED_1; r += z * 0x0000000017499f00; // add y^13 * (20! / 13!) z = (z * y) / FIXED_1; r += z * 0x0000000001a9d480; // add y^14 * (20! / 14!) z = (z * y) / FIXED_1; r += z * 0x00000000001c6380; // add y^15 * (20! / 15!) z = (z * y) / FIXED_1; r += z * 0x000000000001c638; // add y^16 * (20! / 16!) z = (z * y) / FIXED_1; r += z * 0x0000000000001ab8; // add y^17 * (20! / 17!) z = (z * y) / FIXED_1; r += z * 0x000000000000017c; // add y^18 * (20! / 18!) z = (z * y) / FIXED_1; r += z * 0x0000000000000014; // add y^19 * (20! / 19!) z = (z * y) / FIXED_1; r += z * 0x0000000000000001; // add y^20 * (20! / 20!) r = r / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0! // Multiply with the non-residual terms. x = -x; // e ^ -32 if ((x & int256(0x0000000000000000000000000000001000000000000000000000000000000000)) != 0) { r = (r * int256(0x00000000000000000000000000000000000000f1aaddd7742e56d32fb9f99744)) / int256(0x0000000000000000000000000043cbaf42a000812488fc5c220ad7b97bf6e99e); // * e ^ -32 } // e ^ -16 if ((x & int256(0x0000000000000000000000000000000800000000000000000000000000000000)) != 0) { r = (r * int256(0x00000000000000000000000000000000000afe10820813d65dfe6a33c07f738f)) / int256(0x000000000000000000000000000005d27a9f51c31b7c2f8038212a0574779991); // * e ^ -16 } // e ^ -8 if ((x & int256(0x0000000000000000000000000000000400000000000000000000000000000000)) != 0) { r = (r * int256(0x0000000000000000000000000000000002582ab704279e8efd15e0265855c47a)) / int256(0x0000000000000000000000000000001b4c902e273a58678d6d3bfdb93db96d02); // * e ^ -8 } // e ^ -4 if ((x & int256(0x0000000000000000000000000000000200000000000000000000000000000000)) != 0) { r = (r * int256(0x000000000000000000000000000000001152aaa3bf81cb9fdb76eae12d029571)) / int256(0x00000000000000000000000000000003b1cc971a9bb5b9867477440d6d157750); // * e ^ -4 } // e ^ -2 if ((x & int256(0x0000000000000000000000000000000100000000000000000000000000000000)) != 0) { r = (r * int256(0x000000000000000000000000000000002f16ac6c59de6f8d5d6f63c1482a7c86)) / int256(0x000000000000000000000000000000015bf0a8b1457695355fb8ac404e7a79e3); // * e ^ -2 } // e ^ -1 if ((x & int256(0x0000000000000000000000000000000080000000000000000000000000000000)) != 0) { r = (r * int256(0x000000000000000000000000000000004da2cbf1be5827f9eb3ad1aa9866ebb3)) / int256(0x00000000000000000000000000000000d3094c70f034de4b96ff7d5b6f99fcd8); // * e ^ -1 } // e ^ -0.5 if ((x & int256(0x0000000000000000000000000000000040000000000000000000000000000000)) != 0) { r = (r * int256(0x0000000000000000000000000000000063afbe7ab2082ba1a0ae5e4eb1b479dc)) / int256(0x00000000000000000000000000000000a45af1e1f40c333b3de1db4dd55f29a7); // * e ^ -0.5 } // e ^ -0.25 if ((x & int256(0x0000000000000000000000000000000020000000000000000000000000000000)) != 0) { r = (r * int256(0x0000000000000000000000000000000070f5a893b608861e1f58934f97aea57d)) / int256(0x00000000000000000000000000000000910b022db7ae67ce76b441c27035c6a1); // * e ^ -0.25 } // e ^ -0.125 if ((x & int256(0x0000000000000000000000000000000010000000000000000000000000000000)) != 0) { r = (r * int256(0x00000000000000000000000000000000783eafef1c0a8f3978c7f81824d62ebf)) / int256(0x0000000000000000000000000000000088415abbe9a76bead8d00cf112e4d4a8); // * e ^ -0.125 } } /// @dev Returns the multiplication two numbers, reverting on overflow. function _mul(int256 a, int256 b) private pure returns (int256 c) { if (a == 0) { return 0; } c = a * b; if (c / a != b || c / b != a) { revert("overflow"); } } /// @dev Returns the division of two numbers, reverting on division by zero. function _div(int256 a, int256 b) private pure returns (int256 c) { if (b == 0) { revert("overflow"); } if (a == MIN_FIXED_VAL && b == -1) { revert("overflow"); } c = a / b; } /// @dev Adds two numbers, reverting on overflow. function _add(int256 a, int256 b) private pure returns (int256 c) { c = a + b; if ((a < 0 && b < 0 && c > a) || (a > 0 && b > 0 && c < a)) { revert("overflow"); } } }
File 7 of 9: Controller
// SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "./IController.sol"; import "./IManaged.sol"; import "./Governed.sol"; import "./Pausable.sol"; /** * @title Graph Controller contract * @dev Controller is a registry of contracts for convenience. Inspired by Livepeer: * https://github.com/livepeer/protocol/blob/streamflow/contracts/Controller.sol */ contract Controller is Governed, Pausable, IController { // Track contract ids to contract proxy address mapping(bytes32 => address) private registry; event SetContractProxy(bytes32 indexed id, address contractAddress); /** * @dev Contract constructor. */ constructor() { Governed._initialize(msg.sender); _setPaused(true); } /** * @dev Check if the caller is the governor or pause guardian. */ modifier onlyGovernorOrGuardian { require( msg.sender == governor || msg.sender == pauseGuardian, "Only Governor or Guardian can call" ); _; } /** * @notice Getter to access governor */ function getGovernor() external override view returns (address) { return governor; } // -- Registry -- /** * @notice Register contract id and mapped address * @param _id Contract id (keccak256 hash of contract name) * @param _contractAddress Contract address */ function setContractProxy(bytes32 _id, address _contractAddress) external override onlyGovernor { require(_contractAddress != address(0), "Contract address must be set"); registry[_id] = _contractAddress; emit SetContractProxy(_id, _contractAddress); } /** * @notice Unregister a contract address * @param _id Contract id (keccak256 hash of contract name) */ function unsetContractProxy(bytes32 _id) external override onlyGovernor { registry[_id] = address(0); emit SetContractProxy(_id, address(0)); } /** * @notice Get contract proxy address by its id * @param _id Contract id */ function getContractProxy(bytes32 _id) public override view returns (address) { return registry[_id]; } /** * @notice Update contract's controller * @param _id Contract id (keccak256 hash of contract name) * @param _controller Controller address */ function updateController(bytes32 _id, address _controller) external override onlyGovernor { require(_controller != address(0), "Controller must be set"); return IManaged(registry[_id]).setController(_controller); } // -- Pausing -- /** * @notice Change the partial paused state of the contract * Partial pause is intended as a partial pause of the protocol */ function setPartialPaused(bool _partialPaused) external override onlyGovernorOrGuardian { _setPartialPaused(_partialPaused); } /** * @notice Change the paused state of the contract * Full pause most of protocol functions */ function setPaused(bool _paused) external override onlyGovernorOrGuardian { _setPaused(_paused); } /** * @notice Change the Pause Guardian * @param _newPauseGuardian The address of the new Pause Guardian */ function setPauseGuardian(address _newPauseGuardian) external override onlyGovernor { require(_newPauseGuardian != address(0), "PauseGuardian must be set"); _setPauseGuardian(_newPauseGuardian); } /** * @notice Getter to access paused */ function paused() external override view returns (bool) { return _paused; } /** * @notice Getter to access partial pause status */ function partialPaused() external override view returns (bool) { return _partialPaused; } } // SPDX-License-Identifier: MIT pragma solidity >=0.6.12 <0.8.0; interface IController { function getGovernor() external view returns (address); // -- Registry -- function setContractProxy(bytes32 _id, address _contractAddress) external; function unsetContractProxy(bytes32 _id) external; function updateController(bytes32 _id, address _controller) external; function getContractProxy(bytes32 _id) external view returns (address); // -- Pausing -- function setPartialPaused(bool _partialPaused) external; function setPaused(bool _paused) external; function setPauseGuardian(address _newPauseGuardian) external; function paused() external view returns (bool); function partialPaused() external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; interface IManaged { function setController(address _controller) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; /** * @title Graph Governance contract * @dev All contracts that will be owned by a Governor entity should extend this contract. */ contract Governed { // -- State -- address public governor; address public pendingGovernor; // -- Events -- event NewPendingOwnership(address indexed from, address indexed to); event NewOwnership(address indexed from, address indexed to); /** * @dev Check if the caller is the governor. */ modifier onlyGovernor { require(msg.sender == governor, "Only Governor can call"); _; } /** * @dev Initialize the governor to the contract caller. */ function _initialize(address _initGovernor) internal { governor = _initGovernor; } /** * @dev Admin function to begin change of governor. The `_newGovernor` must call * `acceptOwnership` to finalize the transfer. * @param _newGovernor Address of new `governor` */ function transferOwnership(address _newGovernor) external onlyGovernor { require(_newGovernor != address(0), "Governor must be set"); address oldPendingGovernor = pendingGovernor; pendingGovernor = _newGovernor; emit NewPendingOwnership(oldPendingGovernor, pendingGovernor); } /** * @dev Admin function for pending governor to accept role and update governor. * This function must called by the pending governor. */ function acceptOwnership() external { require( pendingGovernor != address(0) && msg.sender == pendingGovernor, "Caller must be pending governor" ); address oldGovernor = governor; address oldPendingGovernor = pendingGovernor; governor = pendingGovernor; pendingGovernor = address(0); emit NewOwnership(oldGovernor, governor); emit NewPendingOwnership(oldPendingGovernor, pendingGovernor); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; contract Pausable { // Partial paused paused exit and enter functions for GRT, but not internal // functions, such as allocating bool internal _partialPaused; // Paused will pause all major protocol functions bool internal _paused; // Time last paused for both pauses uint256 public lastPausePartialTime; uint256 public lastPauseTime; // Pause guardian is a separate entity from the governor that can pause address public pauseGuardian; event PartialPauseChanged(bool isPaused); event PauseChanged(bool isPaused); event NewPauseGuardian(address indexed oldPauseGuardian, address indexed pauseGuardian); /** * @notice Change the partial paused state of the contract */ function _setPartialPaused(bool _toPause) internal { if (_toPause == _partialPaused) { return; } _partialPaused = _toPause; if (_partialPaused) { lastPausePartialTime = block.timestamp; } emit PartialPauseChanged(_partialPaused); } /** * @notice Change the paused state of the contract */ function _setPaused(bool _toPause) internal { if (_toPause == _paused) { return; } _paused = _toPause; if (_paused) { lastPauseTime = block.timestamp; } emit PauseChanged(_paused); } /** * @notice Change the Pause Guardian * @param newPauseGuardian The address of the new Pause Guardian */ function _setPauseGuardian(address newPauseGuardian) internal { address oldPauseGuardian = pauseGuardian; pauseGuardian = newPauseGuardian; emit NewPauseGuardian(oldPauseGuardian, pauseGuardian); } }
File 8 of 9: GraphProxy
// SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/utils/Address.sol"; import "./GraphProxyStorage.sol"; /** * @title Graph Proxy * @dev Graph Proxy contract used to delegate call implementation contracts and support upgrades. * This contract should NOT define storage as it is managed by GraphProxyStorage. * This contract implements a proxy that is upgradeable by an admin. * https://docs.openzeppelin.com/upgrades-plugins/1.x/proxies#transparent-proxies-and-function-clashes */ contract GraphProxy is GraphProxyStorage { /** * @dev Modifier used internally that will delegate the call to the implementation unless * the sender is the admin. */ modifier ifAdmin() { if (msg.sender == _admin()) { _; } else { _fallback(); } } /** * @dev Modifier used internally that will delegate the call to the implementation unless * the sender is the admin or pending implementation. */ modifier ifAdminOrPendingImpl() { if (msg.sender == _admin() || msg.sender == _pendingImplementation()) { _; } else { _fallback(); } } /** * @dev Contract constructor. * @param _impl Address of the initial implementation * @param _admin Address of the proxy admin */ constructor(address _impl, address _admin) { assert(ADMIN_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1)); assert( IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1) ); assert( PENDING_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.pendingImplementation")) - 1) ); _setAdmin(_admin); _setPendingImplementation(_impl); } /** * @dev Returns the current admin. * * NOTE: Only the admin and implementation can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function admin() external ifAdminOrPendingImpl returns (address) { return _admin(); } /** * @dev Returns the current implementation. * * NOTE: Only the admin can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` */ function implementation() external ifAdminOrPendingImpl returns (address) { return _implementation(); } /** * @dev Returns the current pending implementation. * * NOTE: Only the admin can call this function. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x9e5eddc59e0b171f57125ab86bee043d9128098c3a6b9adb4f2e86333c2f6f8c` */ function pendingImplementation() external ifAdminOrPendingImpl returns (address) { return _pendingImplementation(); } /** * @dev Changes the admin of the proxy. * * NOTE: Only the admin can call this function. */ function setAdmin(address _newAdmin) external ifAdmin { require(_newAdmin != address(0), "Cannot change the admin of a proxy to the zero address"); _setAdmin(_newAdmin); } /** * @dev Upgrades to a new implementation contract. * @param _newImplementation Address of implementation contract * * NOTE: Only the admin can call this function. */ function upgradeTo(address _newImplementation) external ifAdmin { _setPendingImplementation(_newImplementation); } /** * @dev Admin function for new implementation to accept its role as implementation. */ function acceptUpgrade() external ifAdminOrPendingImpl { _acceptUpgrade(); } /** * @dev Admin function for new implementation to accept its role as implementation. */ function acceptUpgradeAndCall(bytes calldata data) external ifAdminOrPendingImpl { _acceptUpgrade(); // solhint-disable-next-line avoid-low-level-calls (bool success, ) = _implementation().delegatecall(data); require(success); } /** * @dev Admin function for new implementation to accept its role as implementation. */ function _acceptUpgrade() internal { address _pendingImplementation = _pendingImplementation(); require(Address.isContract(_pendingImplementation), "Implementation must be a contract"); require( _pendingImplementation != address(0) && msg.sender == _pendingImplementation, "Caller must be the pending implementation" ); _setImplementation(_pendingImplementation); _setPendingImplementation(address(0)); } /** * @dev Delegates the current call to implementation. * This function does not return to its internal call site, it will return directly to the * external caller. */ function _fallback() internal { require(msg.sender != _admin(), "Cannot fallback to proxy target"); assembly { // (a) get free memory pointer let ptr := mload(0x40) // (b) get address of the implementation let impl := and(sload(IMPLEMENTATION_SLOT), 0xffffffffffffffffffffffffffffffffffffffff) // (1) copy incoming call data calldatacopy(ptr, 0, calldatasize()) // (2) forward call to logic contract let result := delegatecall(gas(), impl, ptr, calldatasize(), 0, 0) let size := returndatasize() // (3) retrieve return data returndatacopy(ptr, 0, size) // (4) forward return data back to caller switch result case 0 { revert(ptr, size) } default { return(ptr, size) } } } /** * @dev Fallback function that delegates calls to implementation. Will run if no other * function in the contract matches the call data. */ fallback() external payable { _fallback(); } /** * @dev Fallback function that delegates calls to implementation. Will run if call data * is empty. */ receive() external payable { _fallback(); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; /** * @title Graph Proxy Storage * @dev Contract functions related to getting and setting proxy storage. * This contract does not actually define state variables managed by the compiler * but uses fixed slot locations. */ contract GraphProxyStorage { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Storage slot with the address of the pending implementation. * This is the keccak-256 hash of "eip1967.proxy.pendingImplementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant PENDING_IMPLEMENTATION_SLOT = 0x9e5eddc59e0b171f57125ab86bee043d9128098c3a6b9adb4f2e86333c2f6f8c; /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Emitted when pendingImplementation is changed. */ event PendingImplementationUpdated( address indexed oldPendingImplementation, address indexed newPendingImplementation ); /** * @dev Emitted when pendingImplementation is accepted, * which means contract implementation is updated. */ event ImplementationUpdated( address indexed oldImplementation, address indexed newImplementation ); /** * @dev Emitted when the admin account has changed. */ event AdminUpdated(address indexed oldAdmin, address indexed newAdmin); /** * @dev Modifier to check whether the `msg.sender` is the admin. */ modifier onlyAdmin() { require(msg.sender == _admin(), "Caller must be admin"); _; } /** * @return adm The admin slot. */ function _admin() internal view returns (address adm) { bytes32 slot = ADMIN_SLOT; assembly { adm := sload(slot) } } /** * @dev Sets the address of the proxy admin. * @param _newAdmin Address of the new proxy admin */ function _setAdmin(address _newAdmin) internal { bytes32 slot = ADMIN_SLOT; assembly { sstore(slot, _newAdmin) } emit AdminUpdated(_admin(), _newAdmin); } /** * @dev Returns the current implementation. * @return impl Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Returns the current pending implementation. * @return impl Address of the current pending implementation */ function _pendingImplementation() internal view returns (address impl) { bytes32 slot = PENDING_IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Sets the implementation address of the proxy. * @param _newImplementation Address of the new implementation */ function _setImplementation(address _newImplementation) internal { address oldImplementation = _implementation(); bytes32 slot = IMPLEMENTATION_SLOT; assembly { sstore(slot, _newImplementation) } emit ImplementationUpdated(oldImplementation, _newImplementation); } /** * @dev Sets the pending implementation address of the proxy. * @param _newImplementation Address of the new pending implementation */ function _setPendingImplementation(address _newImplementation) internal { address oldPendingImplementation = _pendingImplementation(); bytes32 slot = PENDING_IMPLEMENTATION_SLOT; assembly { sstore(slot, _newImplementation) } emit PendingImplementationUpdated(oldPendingImplementation, _newImplementation); } }
File 9 of 9: EpochManager
// SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/math/SafeMath.sol"; import "../upgrades/GraphUpgradeable.sol"; import "./EpochManagerStorage.sol"; import "./IEpochManager.sol"; /** * @title EpochManager contract * @dev Produce epochs based on a number of blocks to coordinate contracts in the protocol. */ contract EpochManager is EpochManagerV1Storage, GraphUpgradeable, IEpochManager { using SafeMath for uint256; // -- Events -- event EpochRun(uint256 indexed epoch, address caller); event EpochLengthUpdate(uint256 indexed epoch, uint256 epochLength); /** * @dev Initialize this contract. */ function initialize(address _controller, uint256 _epochLength) external onlyImpl { require(_epochLength > 0, "Epoch length cannot be 0"); Managed._initialize(_controller); lastLengthUpdateEpoch = 0; lastLengthUpdateBlock = blockNum(); epochLength = _epochLength; emit EpochLengthUpdate(lastLengthUpdateEpoch, epochLength); } /** * @dev Set the epoch length. * @notice Set epoch length to `_epochLength` blocks * @param _epochLength Epoch length in blocks */ function setEpochLength(uint256 _epochLength) external override onlyGovernor { require(_epochLength > 0, "Epoch length cannot be 0"); require(_epochLength != epochLength, "Epoch length must be different to current"); lastLengthUpdateEpoch = currentEpoch(); lastLengthUpdateBlock = currentEpochBlock(); epochLength = _epochLength; emit EpochLengthUpdate(lastLengthUpdateEpoch, epochLength); } /** * @dev Run a new epoch, should be called once at the start of any epoch. * @notice Perform state changes for the current epoch */ function runEpoch() external override { // Check if already called for the current epoch require(!isCurrentEpochRun(), "Current epoch already run"); lastRunEpoch = currentEpoch(); // Hook for protocol general state updates emit EpochRun(lastRunEpoch, msg.sender); } /** * @dev Return true if the current epoch has already run. * @return Return true if current epoch is the last epoch that has run */ function isCurrentEpochRun() public override view returns (bool) { return lastRunEpoch == currentEpoch(); } /** * @dev Return current block number. * @return Block number */ function blockNum() public override view returns (uint256) { return block.number; } /** * @dev Return blockhash for a block. * @return BlockHash for `_block` number */ function blockHash(uint256 _block) external override view returns (bytes32) { uint256 currentBlock = blockNum(); require(_block < currentBlock, "Can only retrieve past block hashes"); require( currentBlock < 256 || _block >= currentBlock - 256, "Can only retrieve hashes for last 256 blocks" ); return blockhash(_block); } /** * @dev Return the current epoch, it may have not been run yet. * @return The current epoch based on epoch length */ function currentEpoch() public override view returns (uint256) { return lastLengthUpdateEpoch.add(epochsSinceUpdate()); } /** * @dev Return block where the current epoch started. * @return The block number when the current epoch started */ function currentEpochBlock() public override view returns (uint256) { return lastLengthUpdateBlock.add(epochsSinceUpdate().mul(epochLength)); } /** * @dev Return the number of blocks that passed since current epoch started. * @return Blocks that passed since start of epoch */ function currentEpochBlockSinceStart() external override view returns (uint256) { return blockNum() - currentEpochBlock(); } /** * @dev Return the number of epoch that passed since another epoch. * @param _epoch Epoch to use as since epoch value * @return Number of epochs and current epoch */ function epochsSince(uint256 _epoch) external override view returns (uint256) { uint256 epoch = currentEpoch(); return _epoch < epoch ? epoch.sub(_epoch) : 0; } /** * @dev Return number of epochs passed since last epoch length update. * @return The number of epoch that passed since last epoch length update */ function epochsSinceUpdate() public override view returns (uint256) { return blockNum().sub(lastLengthUpdateBlock).div(epochLength); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers. Reverts on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } /** * @dev Returns the integer division of two unsigned integers. Reverts with custom message on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * Reverts with custom message when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "./IGraphProxy.sol"; /** * @title Graph Upgradeable * @dev This contract is intended to be inherited from upgradeable contracts. */ contract GraphUpgradeable { /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Check if the caller is the proxy admin. */ modifier onlyProxyAdmin(IGraphProxy _proxy) { require(msg.sender == _proxy.admin(), "Caller must be the proxy admin"); _; } /** * @dev Check if the caller is the implementation. */ modifier onlyImpl { require(msg.sender == _implementation(), "Caller must be the implementation"); _; } /** * @dev Returns the current implementation. * @return impl Address of the current implementation */ function _implementation() internal view returns (address impl) { bytes32 slot = IMPLEMENTATION_SLOT; assembly { impl := sload(slot) } } /** * @dev Accept to be an implementation of proxy. */ function acceptProxy(IGraphProxy _proxy) external onlyProxyAdmin(_proxy) { _proxy.acceptUpgrade(); } /** * @dev Accept to be an implementation of proxy and then call a function from the new * implementation as specified by `_data`, which should be an encoded function call. This is * useful to initialize new storage variables in the proxied contract. */ function acceptProxyAndCall(IGraphProxy _proxy, bytes calldata _data) external onlyProxyAdmin(_proxy) { _proxy.acceptUpgradeAndCall(_data); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "../governance/Managed.sol"; contract EpochManagerV1Storage is Managed { // -- State -- // Epoch length in blocks uint256 public epochLength; // Epoch that was last run uint256 public lastRunEpoch; // Block and epoch when epoch length was last updated uint256 public lastLengthUpdateEpoch; uint256 public lastLengthUpdateBlock; } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; interface IEpochManager { // -- Configuration -- function setEpochLength(uint256 _epochLength) external; // -- Epochs function runEpoch() external; // -- Getters -- function isCurrentEpochRun() external view returns (bool); function blockNum() external view returns (uint256); function blockHash(uint256 _block) external view returns (bytes32); function currentEpoch() external view returns (uint256); function currentEpochBlock() external view returns (uint256); function currentEpochBlockSinceStart() external view returns (uint256); function epochsSince(uint256 _epoch) external view returns (uint256); function epochsSinceUpdate() external view returns (uint256); } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; interface IGraphProxy { function admin() external returns (address); function setAdmin(address _newAdmin) external; function implementation() external returns (address); function pendingImplementation() external returns (address); function upgradeTo(address _newImplementation) external; function acceptUpgrade() external; function acceptUpgradeAndCall(bytes calldata data) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "./IManaged.sol"; import "./IController.sol"; import "../curation/ICuration.sol"; import "../epochs/IEpochManager.sol"; import "../rewards/IRewardsManager.sol"; import "../staking/IStaking.sol"; import "../token/IGraphToken.sol"; /** * @title Graph Managed contract * @dev The Managed contract provides an interface for contracts to interact with the Controller * Inspired by Livepeer: * https://github.com/livepeer/protocol/blob/streamflow/contracts/Controller.sol */ contract Managed { // Controller that contract is registered with IController public controller; mapping(bytes32 => address) public addressCache; uint256[10] private __gap; event ParameterUpdated(string param); event SetController(address controller); function _notPartialPaused() internal view { require(!controller.paused(), "Paused"); require(!controller.partialPaused(), "Partial-paused"); } function _notPaused() internal view { require(!controller.paused(), "Paused"); } function _onlyGovernor() internal view { require(msg.sender == controller.getGovernor(), "Caller must be Controller governor"); } modifier notPartialPaused { _notPartialPaused(); _; } modifier notPaused { _notPaused(); _; } // Check if sender is controller modifier onlyController() { require(msg.sender == address(controller), "Caller must be Controller"); _; } modifier onlyGovernor() { _onlyGovernor(); _; } /** * @dev Initialize the controller */ function _initialize(address _controller) internal { _setController(_controller); } /** * @notice Set Controller. Only callable by current controller * @param _controller Controller contract address */ function setController(address _controller) external onlyController { _setController(_controller); } /** * @dev Set controller. * @param _controller Controller contract address */ function _setController(address _controller) internal { require(_controller != address(0), "Controller must be set"); controller = IController(_controller); emit SetController(_controller); } /** * @dev Return Curation interface * @return Curation contract registered with Controller */ function curation() internal view returns (ICuration) { return ICuration(controller.getContractProxy(keccak256("Curation"))); } /** * @dev Return EpochManager interface * @return Epoch manager contract registered with Controller */ function epochManager() internal view returns (IEpochManager) { return IEpochManager(controller.getContractProxy(keccak256("EpochManager"))); } /** * @dev Return RewardsManager interface * @return Rewards manager contract registered with Controller */ function rewardsManager() internal view returns (IRewardsManager) { return IRewardsManager(controller.getContractProxy(keccak256("RewardsManager"))); } /** * @dev Return Staking interface * @return Staking contract registered with Controller */ function staking() internal view returns (IStaking) { return IStaking(controller.getContractProxy(keccak256("Staking"))); } /** * @dev Return GraphToken interface * @return Graph token contract registered with Controller */ function graphToken() internal view returns (IGraphToken) { return IGraphToken(controller.getContractProxy(keccak256("GraphToken"))); } } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; interface IManaged { function setController(address _controller) external; } // SPDX-License-Identifier: MIT pragma solidity >=0.6.12 <0.8.0; interface IController { function getGovernor() external view returns (address); // -- Registry -- function setContractProxy(bytes32 _id, address _contractAddress) external; function unsetContractProxy(bytes32 _id) external; function updateController(bytes32 _id, address _controller) external; function getContractProxy(bytes32 _id) external view returns (address); // -- Pausing -- function setPartialPaused(bool _partialPaused) external; function setPaused(bool _paused) external; function setPauseGuardian(address _newPauseGuardian) external; function paused() external view returns (bool); function partialPaused() external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "./IGraphCurationToken.sol"; interface ICuration { // -- Pool -- struct CurationPool { uint256 tokens; // GRT Tokens stored as reserves for the subgraph deployment uint32 reserveRatio; // Ratio for the bonding curve IGraphCurationToken gcs; // Curation token contract for this curation pool } // -- Configuration -- function setDefaultReserveRatio(uint32 _defaultReserveRatio) external; function setMinimumCurationDeposit(uint256 _minimumCurationDeposit) external; function setCurationTaxPercentage(uint32 _percentage) external; // -- Curation -- function mint( bytes32 _subgraphDeploymentID, uint256 _tokensIn, uint256 _signalOutMin ) external returns (uint256, uint256); function burn( bytes32 _subgraphDeploymentID, uint256 _signalIn, uint256 _tokensOutMin ) external returns (uint256); function collect(bytes32 _subgraphDeploymentID, uint256 _tokens) external; // -- Getters -- function isCurated(bytes32 _subgraphDeploymentID) external view returns (bool); function getCuratorSignal(address _curator, bytes32 _subgraphDeploymentID) external view returns (uint256); function getCurationPoolSignal(bytes32 _subgraphDeploymentID) external view returns (uint256); function getCurationPoolTokens(bytes32 _subgraphDeploymentID) external view returns (uint256); function tokensToSignal(bytes32 _subgraphDeploymentID, uint256 _tokensIn) external view returns (uint256, uint256); function signalToTokens(bytes32 _subgraphDeploymentID, uint256 _signalIn) external view returns (uint256); function curationTaxPercentage() external view returns (uint32); } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; interface IRewardsManager { /** * @dev Stores accumulated rewards and snapshots related to a particular SubgraphDeployment. */ struct Subgraph { uint256 accRewardsForSubgraph; uint256 accRewardsForSubgraphSnapshot; uint256 accRewardsPerSignalSnapshot; uint256 accRewardsPerAllocatedToken; } // -- Params -- function setIssuanceRate(uint256 _issuanceRate) external; // -- Denylist -- function setSubgraphAvailabilityOracle(address _subgraphAvailabilityOracle) external; function setDenied(bytes32 _subgraphDeploymentID, bool _deny) external; function setDeniedMany(bytes32[] calldata _subgraphDeploymentID, bool[] calldata _deny) external; function isDenied(bytes32 _subgraphDeploymentID) external view returns (bool); // -- Getters -- function getNewRewardsPerSignal() external view returns (uint256); function getAccRewardsPerSignal() external view returns (uint256); function getAccRewardsForSubgraph(bytes32 _subgraphDeploymentID) external view returns (uint256); function getAccRewardsPerAllocatedToken(bytes32 _subgraphDeploymentID) external view returns (uint256, uint256); function getRewards(address _allocationID) external view returns (uint256); // -- Updates -- function updateAccRewardsPerSignal() external returns (uint256); function takeRewards(address _allocationID) external returns (uint256); // -- Hooks -- function onSubgraphSignalUpdate(bytes32 _subgraphDeploymentID) external returns (uint256); function onSubgraphAllocationUpdate(bytes32 _subgraphDeploymentID) external returns (uint256); } // SPDX-License-Identifier: MIT pragma solidity >=0.6.12 <0.8.0; pragma experimental ABIEncoderV2; interface IStaking { // -- Allocation Data -- /** * @dev Possible states an allocation can be * States: * - Null = indexer == address(0) * - Active = not Null && tokens > 0 * - Closed = Active && closedAtEpoch != 0 * - Finalized = Closed && closedAtEpoch + channelDisputeEpochs > now() * - Claimed = not Null && tokens == 0 */ enum AllocationState { Null, Active, Closed, Finalized, Claimed } /** * @dev Allocate GRT tokens for the purpose of serving queries of a subgraph deployment * An allocation is created in the allocate() function and consumed in claim() */ struct Allocation { address indexer; bytes32 subgraphDeploymentID; uint256 tokens; // Tokens allocated to a SubgraphDeployment uint256 createdAtEpoch; // Epoch when it was created uint256 closedAtEpoch; // Epoch when it was closed uint256 collectedFees; // Collected fees for the allocation uint256 effectiveAllocation; // Effective allocation when closed uint256 accRewardsPerAllocatedToken; // Snapshot used for reward calc } /** * @dev Represents a request to close an allocation with a specific proof of indexing. * This is passed when calling closeAllocationMany to define the closing parameters for * each allocation. */ struct CloseAllocationRequest { address allocationID; bytes32 poi; } // -- Delegation Data -- /** * @dev Delegation pool information. One per indexer. */ struct DelegationPool { uint32 cooldownBlocks; // Blocks to wait before updating parameters uint32 indexingRewardCut; // in PPM uint32 queryFeeCut; // in PPM uint256 updatedAtBlock; // Block when the pool was last updated uint256 tokens; // Total tokens as pool reserves uint256 shares; // Total shares minted in the pool mapping(address => Delegation) delegators; // Mapping of delegator => Delegation } /** * @dev Individual delegation data of a delegator in a pool. */ struct Delegation { uint256 shares; // Shares owned by a delegator in the pool uint256 tokensLocked; // Tokens locked for undelegation uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn } // -- Configuration -- function setMinimumIndexerStake(uint256 _minimumIndexerStake) external; function setThawingPeriod(uint32 _thawingPeriod) external; function setCurationPercentage(uint32 _percentage) external; function setProtocolPercentage(uint32 _percentage) external; function setChannelDisputeEpochs(uint32 _channelDisputeEpochs) external; function setMaxAllocationEpochs(uint32 _maxAllocationEpochs) external; function setRebateRatio(uint32 _alphaNumerator, uint32 _alphaDenominator) external; function setDelegationRatio(uint32 _delegationRatio) external; function setDelegationParameters( uint32 _indexingRewardCut, uint32 _queryFeeCut, uint32 _cooldownBlocks ) external; function setDelegationParametersCooldown(uint32 _blocks) external; function setDelegationUnbondingPeriod(uint32 _delegationUnbondingPeriod) external; function setDelegationTaxPercentage(uint32 _percentage) external; function setSlasher(address _slasher, bool _allowed) external; function setAssetHolder(address _assetHolder, bool _allowed) external; // -- Operation -- function setOperator(address _operator, bool _allowed) external; function isOperator(address _operator, address _indexer) external view returns (bool); // -- Staking -- function stake(uint256 _tokens) external; function stakeTo(address _indexer, uint256 _tokens) external; function unstake(uint256 _tokens) external; function slash( address _indexer, uint256 _tokens, uint256 _reward, address _beneficiary ) external; function withdraw() external; // -- Delegation -- function delegate(address _indexer, uint256 _tokens) external returns (uint256); function undelegate(address _indexer, uint256 _shares) external returns (uint256); function withdrawDelegated(address _indexer, address _newIndexer) external returns (uint256); // -- Channel management and allocations -- function allocate( bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external; function allocateFrom( address _indexer, bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external; function closeAllocation(address _allocationID, bytes32 _poi) external; function closeAllocationMany(CloseAllocationRequest[] calldata _requests) external; function closeAndAllocate( address _oldAllocationID, bytes32 _poi, address _indexer, bytes32 _subgraphDeploymentID, uint256 _tokens, address _allocationID, bytes32 _metadata, bytes calldata _proof ) external; function collect(uint256 _tokens, address _allocationID) external; function claim(address _allocationID, bool _restake) external; function claimMany(address[] calldata _allocationID, bool _restake) external; // -- Getters and calculations -- function hasStake(address _indexer) external view returns (bool); function getIndexerStakedTokens(address _indexer) external view returns (uint256); function getIndexerCapacity(address _indexer) external view returns (uint256); function getAllocation(address _allocationID) external view returns (Allocation memory); function getAllocationState(address _allocationID) external view returns (AllocationState); function isAllocation(address _allocationID) external view returns (bool); function getSubgraphAllocatedTokens(bytes32 _subgraphDeploymentID) external view returns (uint256); function getDelegation(address _indexer, address _delegator) external view returns (Delegation memory); function isDelegator(address _indexer, address _delegator) external view returns (bool); } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IGraphToken is IERC20 { // -- Mint and Burn -- function burn(uint256 amount) external; function mint(address _to, uint256 _amount) external; // -- Mint Admin -- function addMinter(address _account) external; function removeMinter(address _account) external; function renounceMinter() external; function isMinter(address _account) external view returns (bool); // -- Permit -- function permit( address _owner, address _spender, uint256 _value, uint256 _deadline, uint8 _v, bytes32 _r, bytes32 _s ) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.7.3; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IGraphCurationToken is IERC20 { function burnFrom(address _account, uint256 _amount) external; function mint(address _to, uint256 _amount) external; } // SPDX-License-Identifier: MIT pragma solidity ^0.7.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); }