From c0cc1732db85cdd6f14d2780c8ff129be1795eab Mon Sep 17 00:00:00 2001 From: MoonBoi9001 Date: Wed, 18 Sep 2024 14:39:48 +0100 Subject: [PATCH] fix: typographical errors throughout codebase.. (OZ N-07) --- packages/contracts/contracts/curation/Curation.sol | 4 ++-- packages/contracts/contracts/curation/ICuration.sol | 4 ++-- .../contracts/contracts/l2/curation/L2Curation.sol | 6 +++--- .../contracts/contracts/rewards/IRewardsIssuer.sol | 4 ++-- packages/contracts/contracts/staking/Staking.sol | 2 +- packages/contracts/contracts/staking/libs/Stakes.sol | 2 +- .../horizon/contracts/interfaces/ITAPCollector.sol | 2 +- .../interfaces/internal/IHorizonStakingExtension.sol | 2 +- .../interfaces/internal/IHorizonStakingMain.sol | 12 ++++++------ .../interfaces/internal/IHorizonStakingTypes.sol | 4 ++-- .../horizon/contracts/payments/GraphPayments.sol | 2 +- .../horizon/contracts/staking/HorizonStaking.sol | 4 ++-- .../horizon/contracts/staking/HorizonStakingBase.sol | 2 +- .../contracts/staking/HorizonStakingExtension.sol | 6 +++--- .../subgraph-service/contracts/DisputeManager.sol | 2 +- .../contracts/utilities/AllocationManager.sol | 4 ++-- .../token-distribution/contracts/tests/Stakes.sol | 2 +- .../solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json | 2 +- .../solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json | 2 +- .../solcInputs/095bd30babc75057be19228ca1fd7aa4.json | 2 +- .../solcInputs/3c1e469b4f9ba208577ab7c230900006.json | 2 +- .../solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json | 2 +- .../solcInputs/6f5e8f450f52dd96ebb796aa6620fee9.json | 2 +- .../solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json | 2 +- .../solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json | 2 +- .../solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json | 2 +- .../solcInputs/095bd30babc75057be19228ca1fd7aa4.json | 2 +- 27 files changed, 42 insertions(+), 42 deletions(-) diff --git a/packages/contracts/contracts/curation/Curation.sol b/packages/contracts/contracts/curation/Curation.sol index bd3032046..e289d048c 100644 --- a/packages/contracts/contracts/curation/Curation.sol +++ b/packages/contracts/contracts/curation/Curation.sol @@ -257,7 +257,7 @@ contract Curation is CurationV2Storage, GraphUpgradeable { /** * @notice Get the amount of token reserves in a curation pool. - * @param _subgraphDeploymentID Subgraph deployment curation poool + * @param _subgraphDeploymentID Subgraph deployment curation pool * @return Amount of token reserves in the curation pool */ function getCurationPoolTokens(bytes32 _subgraphDeploymentID) external view override returns (uint256) { @@ -286,7 +286,7 @@ contract Curation is CurationV2Storage, GraphUpgradeable { /** * @notice Get the amount of signal in a curation pool. - * @param _subgraphDeploymentID Subgraph deployment curation poool + * @param _subgraphDeploymentID Subgraph deployment curation pool * @return Amount of signal minted for the subgraph deployment */ function getCurationPoolSignal(bytes32 _subgraphDeploymentID) public view override returns (uint256) { diff --git a/packages/contracts/contracts/curation/ICuration.sol b/packages/contracts/contracts/curation/ICuration.sol index fe2f0e929..4f2c2bac5 100644 --- a/packages/contracts/contracts/curation/ICuration.sol +++ b/packages/contracts/contracts/curation/ICuration.sol @@ -84,14 +84,14 @@ interface ICuration { /** * @notice Get the amount of signal in a curation pool. - * @param _subgraphDeploymentID Subgraph deployment curation poool + * @param _subgraphDeploymentID Subgraph deployment curation pool * @return Amount of signal minted for the subgraph deployment */ function getCurationPoolSignal(bytes32 _subgraphDeploymentID) external view returns (uint256); /** * @notice Get the amount of token reserves in a curation pool. - * @param _subgraphDeploymentID Subgraph deployment curation poool + * @param _subgraphDeploymentID Subgraph deployment curation pool * @return Amount of token reserves in the curation pool */ function getCurationPoolTokens(bytes32 _subgraphDeploymentID) external view returns (uint256); diff --git a/packages/contracts/contracts/l2/curation/L2Curation.sol b/packages/contracts/contracts/l2/curation/L2Curation.sol index 3f8728d0a..f6d64209b 100644 --- a/packages/contracts/contracts/l2/curation/L2Curation.sol +++ b/packages/contracts/contracts/l2/curation/L2Curation.sol @@ -147,7 +147,7 @@ contract L2Curation is CurationV3Storage, GraphUpgradeable, IL2Curation { /** * @notice Assign Graph Tokens collected as curation fees to the curation pool reserve. - * @dev This function can only be called by the Staking contract and will do the bookeeping of + * @dev This function can only be called by the Staking contract and will do the Bookkeeping of * transferred tokens into this contract. * @param _subgraphDeploymentID SubgraphDeployment where funds should be allocated as reserves * @param _tokens Amount of Graph Tokens to add to reserves @@ -326,7 +326,7 @@ contract L2Curation is CurationV3Storage, GraphUpgradeable, IL2Curation { /** * @notice Get the amount of token reserves in a curation pool. - * @param _subgraphDeploymentID Subgraph deployment curation poool + * @param _subgraphDeploymentID Subgraph deployment curation pool * @return Amount of token reserves in the curation pool */ function getCurationPoolTokens(bytes32 _subgraphDeploymentID) external view override returns (uint256) { @@ -355,7 +355,7 @@ contract L2Curation is CurationV3Storage, GraphUpgradeable, IL2Curation { /** * @notice Get the amount of signal in a curation pool. - * @param _subgraphDeploymentID Subgraph deployment curation poool + * @param _subgraphDeploymentID Subgraph deployment curation pool * @return Amount of signal minted for the subgraph deployment */ function getCurationPoolSignal(bytes32 _subgraphDeploymentID) public view override returns (uint256) { diff --git a/packages/contracts/contracts/rewards/IRewardsIssuer.sol b/packages/contracts/contracts/rewards/IRewardsIssuer.sol index 705ce8db8..fe6963fa7 100644 --- a/packages/contracts/contracts/rewards/IRewardsIssuer.sol +++ b/packages/contracts/contracts/rewards/IRewardsIssuer.sol @@ -33,9 +33,9 @@ interface IRewardsIssuer { function getSubgraphAllocatedTokens(bytes32 _subgraphDeploymentId) external view returns (uint256); /** - * @notice Wether or not an allocation is active (i.e open) + * @notice Whether or not an allocation is active (i.e open) * @param _allocationId Allocation Id - * @return Wether or not the allocation is active + * @return Whether or not the allocation is active */ function isActiveAllocation(address _allocationId) external view returns (bool); } diff --git a/packages/contracts/contracts/staking/Staking.sol b/packages/contracts/contracts/staking/Staking.sol index d44d4767e..6aef50efc 100644 --- a/packages/contracts/contracts/staking/Staking.sol +++ b/packages/contracts/contracts/staking/Staking.sol @@ -912,7 +912,7 @@ abstract contract Staking is StakingV4Storage, GraphUpgradeable, IStakingBase, M 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 + // Then we call collect() to do the transfer Bookkeeping rewardsManager().onSubgraphSignalUpdate(_subgraphDeploymentID); TokenUtils.pushTokens(_graphToken, address(curation), curationFees); curation.collect(_subgraphDeploymentID, curationFees); diff --git a/packages/contracts/contracts/staking/libs/Stakes.sol b/packages/contracts/contracts/staking/libs/Stakes.sol index b0524b14c..e856cdec1 100644 --- a/packages/contracts/contracts/staking/libs/Stakes.sol +++ b/packages/contracts/contracts/staking/libs/Stakes.sol @@ -85,7 +85,7 @@ library Stakes { /** * @dev Unlock tokens. * @param stake Stake data - * @param _tokens Amount of tokens to unkock + * @param _tokens Amount of tokens to unlock */ function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal { stake.tokensLocked = stake.tokensLocked.sub(_tokens); diff --git a/packages/horizon/contracts/interfaces/ITAPCollector.sol b/packages/horizon/contracts/interfaces/ITAPCollector.sol index e7b5bc4fd..6cfc16485 100644 --- a/packages/horizon/contracts/interfaces/ITAPCollector.sol +++ b/packages/horizon/contracts/interfaces/ITAPCollector.sol @@ -35,7 +35,7 @@ interface ITAPCollector is IPaymentsCollector { } /** - * Trown when the caller is not the data service the RAV was issued to + * Thrown when the caller is not the data service the RAV was issued to * @param caller The address of the caller * @param dataService The address of the data service */ diff --git a/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol b/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol index 6e29cb5c9..fcf70cc32 100644 --- a/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol +++ b/packages/horizon/contracts/interfaces/internal/IHorizonStakingExtension.sol @@ -162,7 +162,7 @@ interface IHorizonStakingExtension is IRewardsIssuer, IL2StakingBase { function isAllocation(address allocationID) external view returns (bool); /** - * @notice Retrun the time in blocks to unstake + * @notice Return the time in blocks to unstake * @return Thawing period in blocks */ // solhint-disable-next-line func-name-mixedcase diff --git a/packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol b/packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol index 418459d9f..ed7e90804 100644 --- a/packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol +++ b/packages/horizon/contracts/interfaces/internal/IHorizonStakingMain.sol @@ -253,7 +253,7 @@ interface IHorizonStakingMain { * @param thawRequestId The ID of the thaw request * @param tokens The amount of tokens being released * @param shares The amount of shares being released - * @param thawingUntil The timestamp until the stake has thawn + * @param thawingUntil The timestamp until the stake has thawed */ event ThawRequestFulfilled(bytes32 indexed thawRequestId, uint256 tokens, uint256 shares, uint64 thawingUntil); @@ -417,7 +417,7 @@ interface IHorizonStakingMain { error HorizonStakingStillThawing(uint256 until); /** - * @notice Thrown when a service provider attempts to operate on verifieres that are not allowed. + * @notice Thrown when a service provider attempts to operate on verifiers that are not allowed. * @dev Only applies to stake from locked wallets. */ error HorizonStakingVerifierNotAllowed(address verifier); @@ -578,7 +578,7 @@ interface IHorizonStakingMain { /** * @notice Remove tokens from a provision and move them back to the service provider's idle stake. * @dev The parameter `nThawRequests` can be set to a non zero value to fulfill a specific number of thaw - * requests in the event that fulfulling all of them results in a gas limit error. + * requests in the event that fulfilling all of them results in a gas limit error. * * Requirements: * - Must have previously initiated a thaw request using {thaw}. @@ -704,7 +704,7 @@ interface IHorizonStakingMain { * @notice Withdraw undelegated tokens from a provision after thawing. * Tokens can be automatically re-delegated to another provision by setting `newServiceProvider`. * @dev The parameter `nThawRequests` can be set to a non zero value to fulfill a specific number of thaw - * requests in the event that fulfulling all of them results in a gas limit error. + * requests in the event that fulfilling all of them results in a gas limit error. * * Requirements: * - Must have previously initiated a thaw request using {undelegate}. @@ -744,7 +744,7 @@ interface IHorizonStakingMain { /** * @notice Delegate tokens to the subgraph data service provision. * This function is for backwards compatibility with the legacy staking contract. - * It only allows delegting to the subgraph data service and DOES NOT have slippage protection. + * It only allows delegating to the subgraph data service and DOES NOT have slippage protection. * @dev See {delegate}. * @param serviceProvider The service provider address * @param tokens The amount of tokens to delegate @@ -754,7 +754,7 @@ interface IHorizonStakingMain { /** * @notice Undelegate tokens from the subgraph data service provision and start thawing them. * This function is for backwards compatibility with the legacy staking contract. - * It only allows undelegting from the subgraph data service. + * It only allows undelegating from the subgraph data service. * @dev See {undelegate}. * @param serviceProvider The service provider address * @param shares The amount of shares to undelegate diff --git a/packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol b/packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol index eef8098e6..42b5588ef 100644 --- a/packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol +++ b/packages/horizon/contracts/interfaces/internal/IHorizonStakingTypes.sol @@ -39,7 +39,7 @@ interface IHorizonStakingTypes { * @dev See {ServiceProviderInternal} for the actual storage representation */ struct ServiceProvider { - // Total amount of tokens on the provider stake (only staked by the provider, inludes all provisions) + // Total amount of tokens on the provider stake (only staked by the provider, includes all provisions) uint256 tokensStaked; // Total amount of tokens locked in provisions (only staked by the provider) uint256 tokensProvisioned; @@ -50,7 +50,7 @@ interface IHorizonStakingTypes { * @dev It contains deprecated fields from the `Indexer` struct to maintain storage compatibility. */ struct ServiceProviderInternal { - // Total amount of tokens on the provider stake (only staked by the provider, inludes all provisions) + // Total amount of tokens on the provider stake (only staked by the provider, includes all provisions) uint256 tokensStaked; // (Deprecated) Tokens used in allocations uint256 __DEPRECATED_tokensAllocated; // solhint-disable-line graph/leading-underscore diff --git a/packages/horizon/contracts/payments/GraphPayments.sol b/packages/horizon/contracts/payments/GraphPayments.sol index b7cb34db7..05fbcb066 100644 --- a/packages/horizon/contracts/payments/GraphPayments.sol +++ b/packages/horizon/contracts/payments/GraphPayments.sol @@ -24,7 +24,7 @@ contract GraphPayments is Initializable, MulticallUpgradeable, GraphDirectory, I /** * @notice Constructor for the {GraphPayments} contract - * @dev This contract is upgradeable however we stil use the constructor to set + * @dev This contract is upgradeable however we still use the constructor to set * a few immutable variables. * @param controller The address of the Graph controller * @param protocolPaymentCut The protocol tax in PPM diff --git a/packages/horizon/contracts/staking/HorizonStaking.sol b/packages/horizon/contracts/staking/HorizonStaking.sol index 74b2d8d09..e36d33a73 100644 --- a/packages/horizon/contracts/staking/HorizonStaking.sol +++ b/packages/horizon/contracts/staking/HorizonStaking.sol @@ -62,7 +62,7 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain { } /** - * @dev The staking contract is upgradeable however we stil use the constructor to set + * @dev The staking contract is upgradeable however we still use the constructor to set * a few immutable variables. * @param controller The address of the Graph controller contract. * @param stakingExtensionAddress The address of the staking extension contract. @@ -903,7 +903,7 @@ contract HorizonStaking is HorizonStakingBase, IHorizonStakingMain { * * @param _thawRequestId The ID of the current thaw request * @param _acc The accumulator data for the thaw requests being fulfilled - * @return Wether the thaw request is still thawing, indicating that the traversal should continue or stop. + * @return Whether the thaw request is still thawing, indicating that the traversal should continue or stop. * @return The updated accumulator data */ function _fulfillThawRequest(bytes32 _thawRequestId, bytes memory _acc) private returns (bool, bytes memory) { diff --git a/packages/horizon/contracts/staking/HorizonStakingBase.sol b/packages/horizon/contracts/staking/HorizonStakingBase.sol index b3e94c7ea..b55b39fd8 100644 --- a/packages/horizon/contracts/staking/HorizonStakingBase.sol +++ b/packages/horizon/contracts/staking/HorizonStakingBase.sol @@ -39,7 +39,7 @@ abstract contract HorizonStakingBase is address internal immutable SUBGRAPH_DATA_SERVICE_ADDRESS; /** - * @dev The staking contract is upgradeable however we stil use the constructor to set + * @dev The staking contract is upgradeable however we still use the constructor to set * a few immutable variables. * @param controller The address of the Graph controller contract. * @param subgraphDataServiceAddress The address of the subgraph data service. diff --git a/packages/horizon/contracts/staking/HorizonStakingExtension.sol b/packages/horizon/contracts/staking/HorizonStakingExtension.sol index ba7eb3a5b..66efe5521 100644 --- a/packages/horizon/contracts/staking/HorizonStakingExtension.sol +++ b/packages/horizon/contracts/staking/HorizonStakingExtension.sol @@ -41,7 +41,7 @@ contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizon } /** - * @dev The staking contract is upgradeable however we stil use the constructor to set + * @dev The staking contract is upgradeable however we still use the constructor to set * a few immutable variables. * @param controller The address of the Graph controller contract. * @param subgraphDataServiceAddress The address of the subgraph data service. @@ -296,7 +296,7 @@ contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizon } /** - * @notice Retrun the time in blocks to unstake + * @notice Return the time in blocks to unstake * Deprecated, now enforced by each data service (verifier) * @return Thawing period in blocks */ @@ -569,7 +569,7 @@ contract HorizonStakingExtension is HorizonStakingBase, IL2StakingBase, IHorizon 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 + // Then we call collect() to do the transfer Bookkeeping _graphRewardsManager().onSubgraphSignalUpdate(_subgraphDeploymentID); _graphToken().pushTokens(address(curation), curationFees); curation.collect(_subgraphDeploymentID, curationFees); diff --git a/packages/subgraph-service/contracts/DisputeManager.sol b/packages/subgraph-service/contracts/DisputeManager.sol index f48c20463..8bf0f363b 100644 --- a/packages/subgraph-service/contracts/DisputeManager.sol +++ b/packages/subgraph-service/contracts/DisputeManager.sol @@ -272,7 +272,7 @@ contract DisputeManager is } /** - * @notice Once the dispute period ends, if the disput status remains Pending, + * @notice Once the dispute period ends, if the dispute status remains Pending, * the fisherman can cancel the dispute and get back their initial deposit. * @dev Cancel a dispute with Id `disputeId` * @param disputeId Id of the dispute to be cancelled diff --git a/packages/subgraph-service/contracts/utilities/AllocationManager.sol b/packages/subgraph-service/contracts/utilities/AllocationManager.sol index 2e40f28d3..e0d3ea036 100644 --- a/packages/subgraph-service/contracts/utilities/AllocationManager.sol +++ b/packages/subgraph-service/contracts/utilities/AllocationManager.sol @@ -142,7 +142,7 @@ abstract contract AllocationManager is EIP712Upgradeable, GraphDirectory, Alloca error AllocationManagerZeroTokensAllocation(address allocationId); /** - * @notice Thrown when attempting to collect indexing rewards on a closed allocationl + * @notice Thrown when attempting to collect indexing rewards on a closed allocation * @param allocationId The id of the allocation */ error AllocationManagerAllocationClosed(address allocationId); @@ -454,7 +454,7 @@ abstract contract AllocationManager is EIP712Upgradeable, GraphDirectory, Alloca } /** - * @notice Verifies ownsership of an allocation id by verifying an EIP712 allocation proof + * @notice Verifies ownership of an allocation id by verifying an EIP712 allocation proof * @dev Requirements: * - Signer must be the allocation id address * @param _indexer The address of the indexer diff --git a/packages/token-distribution/contracts/tests/Stakes.sol b/packages/token-distribution/contracts/tests/Stakes.sol index bf140aa8f..734c1d28e 100644 --- a/packages/token-distribution/contracts/tests/Stakes.sol +++ b/packages/token-distribution/contracts/tests/Stakes.sol @@ -78,7 +78,7 @@ library Stakes { /** * @dev Unlock tokens. * @param stake Stake data - * @param _tokens Amount of tokens to unkock + * @param _tokens Amount of tokens to unlock */ function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal { stake.tokensLocked = stake.tokensLocked.sub(_tokens); diff --git a/packages/token-distribution/deployments/arbitrum-goerli/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json b/packages/token-distribution/deployments/arbitrum-goerli/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json index 4eda754ae..1bd0bdd60 100644 --- a/packages/token-distribution/deployments/arbitrum-goerli/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json +++ b/packages/token-distribution/deployments/arbitrum-goerli/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json @@ -113,7 +113,7 @@ "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { ITokenGateway } from \"../arbitrum//ITokenGateway.sol\";\nimport { GraphTokenMock } from \"./GraphTokenMock.sol\";\nimport { ICallhookReceiver } from \"../ICallhookReceiver.sol\";\n\n/**\n * @title L2 Token Gateway mock contract\n * @dev Used for testing purposes, DO NOT USE IN PRODUCTION\n */\ncontract L2TokenGatewayMock is Ownable {\n /// Address of the L1 GRT contract\n address public immutable l1Token;\n /// Address of the L2 GRT contract\n address public immutable l2Token;\n /// Next ID to return when sending an outboundTransfer\n uint256 public nextId;\n\n /// @dev Emitted when a (fake) transaction to L1 is created\n event FakeTxToL1(address from, bytes outboundCalldata);\n /// @dev Emitted when a (fake) retryable ticket is received from L1\n event DepositFinalized(address token, address indexed from, address indexed to, uint256 amount);\n\n /// @dev Emitted when an outbound transfer is initiated, i.e. tokens are withdrawn to L1 from L2\n event WithdrawalInitiated(\n address l1Token,\n address indexed from,\n address indexed to,\n uint256 indexed sequenceNumber,\n uint256 amount\n );\n\n /**\n * @notice L2 Token Gateway Contract Constructor.\n * @param _l1Token Address of the L1 GRT contract\n * @param _l2Token Address of the L2 GRT contract\n */\n constructor(address _l1Token, address _l2Token) {\n l1Token = _l1Token;\n l2Token = _l2Token;\n }\n\n /**\n * @notice Creates and sends a (fake) transfer of GRT to L1.\n * This mock will actually just emit an event with parameters equivalent to what the real L2GraphTokenGateway\n * would send to L1.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _to Recipient address on L2\n * @param _amount Amount of tokens to tranfer\n * @param _data Encoded maxSubmissionCost and sender address along with additional calldata\n * @return ID of the L2-L1 message (incrementing on every call)\n */\n function outboundTransfer(\n address _l1Token,\n address _to,\n uint256 _amount,\n uint256,\n uint256,\n bytes calldata _data\n ) external payable returns (bytes memory) {\n require(_l1Token == l1Token, \"INVALID_L1_TOKEN\");\n require(_amount > 0, \"INVALID_ZERO_AMOUNT\");\n require(_to != address(0), \"INVALID_DESTINATION\");\n\n // nested scopes to avoid stack too deep errors\n address from;\n uint256 id = nextId;\n nextId += 1;\n {\n bytes memory outboundCalldata;\n {\n bytes memory extraData;\n (from, extraData) = _parseOutboundData(_data);\n\n require(msg.value == 0, \"!value\");\n require(extraData.length == 0, \"!extraData\");\n outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData);\n }\n {\n // burn tokens from the sender, they will be released from escrow in L1\n GraphTokenMock(l2Token).bridgeBurn(from, _amount);\n\n emit FakeTxToL1(from, outboundCalldata);\n }\n }\n emit WithdrawalInitiated(_l1Token, from, _to, id, _amount);\n\n return abi.encode(id);\n }\n\n /**\n * @notice (Mock) Receives withdrawn tokens from L1\n * Implements calling callhooks if data is non-empty.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _from Address of the sender\n * @param _to Recipient address on L1\n * @param _amount Amount of tokens transferred\n * @param _data Additional calldata, will trigger an onTokenTransfer call if non-empty\n */\n function finalizeInboundTransfer(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes calldata _data\n ) external payable {\n require(_l1Token == l1Token, \"TOKEN_NOT_GRT\");\n require(msg.value == 0, \"INVALID_NONZERO_VALUE\");\n\n GraphTokenMock(l2Token).bridgeMint(_to, _amount);\n\n if (_data.length > 0) {\n ICallhookReceiver(_to).onTokenTransfer(_from, _amount, _data);\n }\n\n emit DepositFinalized(_l1Token, _from, _to, _amount);\n }\n\n /**\n * @notice Calculate the L2 address of a bridged token\n * @dev In our case, this would only work for GRT.\n * @param l1ERC20 address of L1 GRT contract\n * @return L2 address of the bridged GRT token\n */\n function calculateL2TokenAddress(address l1ERC20) public view returns (address) {\n if (l1ERC20 != l1Token) {\n return address(0);\n }\n return l2Token;\n }\n\n /**\n * @notice Creates calldata required to create a tx to L1\n * @param _l1Token Address of the Graph token contract on L1\n * @param _from Address on L2 from which we're transferring tokens\n * @param _to Address on L1 to which we're transferring tokens\n * @param _amount Amount of GRT to transfer\n * @param _data Additional call data for the L1 transaction, which must be empty\n * @return Encoded calldata (including function selector) for the L1 transaction\n */\n function getOutboundCalldata(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes memory _data\n ) public pure returns (bytes memory) {\n return\n abi.encodeWithSelector(\n ITokenGateway.finalizeInboundTransfer.selector,\n _l1Token,\n _from,\n _to,\n _amount,\n abi.encode(0, _data)\n );\n }\n\n /**\n * @dev Decodes calldata required for transfer of tokens to L1.\n * extraData can be left empty\n * @param _data Encoded callhook data\n * @return Sender of the tx\n * @return Any other data sent to L1\n */\n function _parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) {\n address from;\n bytes memory extraData;\n // The mock doesn't take messages from the Router\n from = msg.sender;\n extraData = _data;\n return (from, extraData);\n }\n}\n" }, "contracts/tests/Stakes.sol": { - "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "contracts/tests/StakingMock.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n receive() external payable {}\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" diff --git a/packages/token-distribution/deployments/arbitrum-one/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json b/packages/token-distribution/deployments/arbitrum-one/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json index 4eda754ae..1bd0bdd60 100644 --- a/packages/token-distribution/deployments/arbitrum-one/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json +++ b/packages/token-distribution/deployments/arbitrum-one/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json @@ -113,7 +113,7 @@ "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { ITokenGateway } from \"../arbitrum//ITokenGateway.sol\";\nimport { GraphTokenMock } from \"./GraphTokenMock.sol\";\nimport { ICallhookReceiver } from \"../ICallhookReceiver.sol\";\n\n/**\n * @title L2 Token Gateway mock contract\n * @dev Used for testing purposes, DO NOT USE IN PRODUCTION\n */\ncontract L2TokenGatewayMock is Ownable {\n /// Address of the L1 GRT contract\n address public immutable l1Token;\n /// Address of the L2 GRT contract\n address public immutable l2Token;\n /// Next ID to return when sending an outboundTransfer\n uint256 public nextId;\n\n /// @dev Emitted when a (fake) transaction to L1 is created\n event FakeTxToL1(address from, bytes outboundCalldata);\n /// @dev Emitted when a (fake) retryable ticket is received from L1\n event DepositFinalized(address token, address indexed from, address indexed to, uint256 amount);\n\n /// @dev Emitted when an outbound transfer is initiated, i.e. tokens are withdrawn to L1 from L2\n event WithdrawalInitiated(\n address l1Token,\n address indexed from,\n address indexed to,\n uint256 indexed sequenceNumber,\n uint256 amount\n );\n\n /**\n * @notice L2 Token Gateway Contract Constructor.\n * @param _l1Token Address of the L1 GRT contract\n * @param _l2Token Address of the L2 GRT contract\n */\n constructor(address _l1Token, address _l2Token) {\n l1Token = _l1Token;\n l2Token = _l2Token;\n }\n\n /**\n * @notice Creates and sends a (fake) transfer of GRT to L1.\n * This mock will actually just emit an event with parameters equivalent to what the real L2GraphTokenGateway\n * would send to L1.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _to Recipient address on L2\n * @param _amount Amount of tokens to tranfer\n * @param _data Encoded maxSubmissionCost and sender address along with additional calldata\n * @return ID of the L2-L1 message (incrementing on every call)\n */\n function outboundTransfer(\n address _l1Token,\n address _to,\n uint256 _amount,\n uint256,\n uint256,\n bytes calldata _data\n ) external payable returns (bytes memory) {\n require(_l1Token == l1Token, \"INVALID_L1_TOKEN\");\n require(_amount > 0, \"INVALID_ZERO_AMOUNT\");\n require(_to != address(0), \"INVALID_DESTINATION\");\n\n // nested scopes to avoid stack too deep errors\n address from;\n uint256 id = nextId;\n nextId += 1;\n {\n bytes memory outboundCalldata;\n {\n bytes memory extraData;\n (from, extraData) = _parseOutboundData(_data);\n\n require(msg.value == 0, \"!value\");\n require(extraData.length == 0, \"!extraData\");\n outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData);\n }\n {\n // burn tokens from the sender, they will be released from escrow in L1\n GraphTokenMock(l2Token).bridgeBurn(from, _amount);\n\n emit FakeTxToL1(from, outboundCalldata);\n }\n }\n emit WithdrawalInitiated(_l1Token, from, _to, id, _amount);\n\n return abi.encode(id);\n }\n\n /**\n * @notice (Mock) Receives withdrawn tokens from L1\n * Implements calling callhooks if data is non-empty.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _from Address of the sender\n * @param _to Recipient address on L1\n * @param _amount Amount of tokens transferred\n * @param _data Additional calldata, will trigger an onTokenTransfer call if non-empty\n */\n function finalizeInboundTransfer(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes calldata _data\n ) external payable {\n require(_l1Token == l1Token, \"TOKEN_NOT_GRT\");\n require(msg.value == 0, \"INVALID_NONZERO_VALUE\");\n\n GraphTokenMock(l2Token).bridgeMint(_to, _amount);\n\n if (_data.length > 0) {\n ICallhookReceiver(_to).onTokenTransfer(_from, _amount, _data);\n }\n\n emit DepositFinalized(_l1Token, _from, _to, _amount);\n }\n\n /**\n * @notice Calculate the L2 address of a bridged token\n * @dev In our case, this would only work for GRT.\n * @param l1ERC20 address of L1 GRT contract\n * @return L2 address of the bridged GRT token\n */\n function calculateL2TokenAddress(address l1ERC20) public view returns (address) {\n if (l1ERC20 != l1Token) {\n return address(0);\n }\n return l2Token;\n }\n\n /**\n * @notice Creates calldata required to create a tx to L1\n * @param _l1Token Address of the Graph token contract on L1\n * @param _from Address on L2 from which we're transferring tokens\n * @param _to Address on L1 to which we're transferring tokens\n * @param _amount Amount of GRT to transfer\n * @param _data Additional call data for the L1 transaction, which must be empty\n * @return Encoded calldata (including function selector) for the L1 transaction\n */\n function getOutboundCalldata(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes memory _data\n ) public pure returns (bytes memory) {\n return\n abi.encodeWithSelector(\n ITokenGateway.finalizeInboundTransfer.selector,\n _l1Token,\n _from,\n _to,\n _amount,\n abi.encode(0, _data)\n );\n }\n\n /**\n * @dev Decodes calldata required for transfer of tokens to L1.\n * extraData can be left empty\n * @param _data Encoded callhook data\n * @return Sender of the tx\n * @return Any other data sent to L1\n */\n function _parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) {\n address from;\n bytes memory extraData;\n // The mock doesn't take messages from the Router\n from = msg.sender;\n extraData = _data;\n return (from, extraData);\n }\n}\n" }, "contracts/tests/Stakes.sol": { - "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "contracts/tests/StakingMock.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n receive() external payable {}\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" diff --git a/packages/token-distribution/deployments/arbitrum-sepolia/solcInputs/095bd30babc75057be19228ca1fd7aa4.json b/packages/token-distribution/deployments/arbitrum-sepolia/solcInputs/095bd30babc75057be19228ca1fd7aa4.json index e7a4b87c0..1adcf2491 100644 --- a/packages/token-distribution/deployments/arbitrum-sepolia/solcInputs/095bd30babc75057be19228ca1fd7aa4.json +++ b/packages/token-distribution/deployments/arbitrum-sepolia/solcInputs/095bd30babc75057be19228ca1fd7aa4.json @@ -113,7 +113,7 @@ "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { ITokenGateway } from \"../arbitrum//ITokenGateway.sol\";\nimport { GraphTokenMock } from \"./GraphTokenMock.sol\";\nimport { ICallhookReceiver } from \"../ICallhookReceiver.sol\";\n\n/**\n * @title L2 Token Gateway mock contract\n * @dev Used for testing purposes, DO NOT USE IN PRODUCTION\n */\ncontract L2TokenGatewayMock is Ownable {\n /// Address of the L1 GRT contract\n address public immutable l1Token;\n /// Address of the L2 GRT contract\n address public immutable l2Token;\n /// Next ID to return when sending an outboundTransfer\n uint256 public nextId;\n\n /// @dev Emitted when a (fake) transaction to L1 is created\n event FakeTxToL1(address from, bytes outboundCalldata);\n /// @dev Emitted when a (fake) retryable ticket is received from L1\n event DepositFinalized(address token, address indexed from, address indexed to, uint256 amount);\n\n /// @dev Emitted when an outbound transfer is initiated, i.e. tokens are withdrawn to L1 from L2\n event WithdrawalInitiated(\n address l1Token,\n address indexed from,\n address indexed to,\n uint256 indexed sequenceNumber,\n uint256 amount\n );\n\n /**\n * @notice L2 Token Gateway Contract Constructor.\n * @param _l1Token Address of the L1 GRT contract\n * @param _l2Token Address of the L2 GRT contract\n */\n constructor(address _l1Token, address _l2Token) {\n l1Token = _l1Token;\n l2Token = _l2Token;\n }\n\n /**\n * @notice Creates and sends a (fake) transfer of GRT to L1.\n * This mock will actually just emit an event with parameters equivalent to what the real L2GraphTokenGateway\n * would send to L1.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _to Recipient address on L2\n * @param _amount Amount of tokens to tranfer\n * @param _data Encoded maxSubmissionCost and sender address along with additional calldata\n * @return ID of the L2-L1 message (incrementing on every call)\n */\n function outboundTransfer(\n address _l1Token,\n address _to,\n uint256 _amount,\n uint256,\n uint256,\n bytes calldata _data\n ) external payable returns (bytes memory) {\n require(_l1Token == l1Token, \"INVALID_L1_TOKEN\");\n require(_amount > 0, \"INVALID_ZERO_AMOUNT\");\n require(_to != address(0), \"INVALID_DESTINATION\");\n\n // nested scopes to avoid stack too deep errors\n address from;\n uint256 id = nextId;\n nextId += 1;\n {\n bytes memory outboundCalldata;\n {\n bytes memory extraData;\n (from, extraData) = _parseOutboundData(_data);\n\n require(msg.value == 0, \"!value\");\n require(extraData.length == 0, \"!extraData\");\n outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData);\n }\n {\n // burn tokens from the sender, they will be released from escrow in L1\n GraphTokenMock(l2Token).bridgeBurn(from, _amount);\n\n emit FakeTxToL1(from, outboundCalldata);\n }\n }\n emit WithdrawalInitiated(_l1Token, from, _to, id, _amount);\n\n return abi.encode(id);\n }\n\n /**\n * @notice (Mock) Receives withdrawn tokens from L1\n * Implements calling callhooks if data is non-empty.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _from Address of the sender\n * @param _to Recipient address on L1\n * @param _amount Amount of tokens transferred\n * @param _data Additional calldata, will trigger an onTokenTransfer call if non-empty\n */\n function finalizeInboundTransfer(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes calldata _data\n ) external payable {\n require(_l1Token == l1Token, \"TOKEN_NOT_GRT\");\n require(msg.value == 0, \"INVALID_NONZERO_VALUE\");\n\n GraphTokenMock(l2Token).bridgeMint(_to, _amount);\n\n if (_data.length > 0) {\n ICallhookReceiver(_to).onTokenTransfer(_from, _amount, _data);\n }\n\n emit DepositFinalized(_l1Token, _from, _to, _amount);\n }\n\n /**\n * @notice Calculate the L2 address of a bridged token\n * @dev In our case, this would only work for GRT.\n * @param l1ERC20 address of L1 GRT contract\n * @return L2 address of the bridged GRT token\n */\n function calculateL2TokenAddress(address l1ERC20) public view returns (address) {\n if (l1ERC20 != l1Token) {\n return address(0);\n }\n return l2Token;\n }\n\n /**\n * @notice Creates calldata required to create a tx to L1\n * @param _l1Token Address of the Graph token contract on L1\n * @param _from Address on L2 from which we're transferring tokens\n * @param _to Address on L1 to which we're transferring tokens\n * @param _amount Amount of GRT to transfer\n * @param _data Additional call data for the L1 transaction, which must be empty\n * @return Encoded calldata (including function selector) for the L1 transaction\n */\n function getOutboundCalldata(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes memory _data\n ) public pure returns (bytes memory) {\n return\n abi.encodeWithSelector(\n ITokenGateway.finalizeInboundTransfer.selector,\n _l1Token,\n _from,\n _to,\n _amount,\n abi.encode(0, _data)\n );\n }\n\n /**\n * @dev Decodes calldata required for transfer of tokens to L1.\n * extraData can be left empty\n * @param _data Encoded callhook data\n * @return Sender of the tx\n * @return Any other data sent to L1\n */\n function _parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) {\n address from;\n bytes memory extraData;\n // The mock doesn't take messages from the Router\n from = msg.sender;\n extraData = _data;\n return (from, extraData);\n }\n}\n" }, "contracts/tests/Stakes.sol": { - "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "contracts/tests/StakingMock.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n receive() external payable {}\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" diff --git a/packages/token-distribution/deployments/goerli/solcInputs/3c1e469b4f9ba208577ab7c230900006.json b/packages/token-distribution/deployments/goerli/solcInputs/3c1e469b4f9ba208577ab7c230900006.json index 276324157..9ef2bdb95 100644 --- a/packages/token-distribution/deployments/goerli/solcInputs/3c1e469b4f9ba208577ab7c230900006.json +++ b/packages/token-distribution/deployments/goerli/solcInputs/3c1e469b4f9ba208577ab7c230900006.json @@ -44,7 +44,7 @@ "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" }, "contracts/Stakes.sol": { - "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(\n Stakes.Indexer storage stake,\n uint256 _tokens,\n uint256 _period\n ) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity)\n internal\n pure\n returns (uint256)\n {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(\n Stakes.Indexer storage stake,\n uint256 _tokens,\n uint256 _period\n ) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity)\n internal\n pure\n returns (uint256)\n {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "@openzeppelin/contracts/token/ERC20/ERC20.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../../utils/Context.sol\";\nimport \"./IERC20.sol\";\nimport \"../../math/SafeMath.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20 {\n using SafeMath for uint256;\n\n mapping (address => uint256) private _balances;\n\n mapping (address => mapping (address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for {name} and {symbol}, initializes {decimals} with\n * a default value of 18.\n *\n * To select a different value for {decimals}, use {_setupDecimals}.\n *\n * All three of these values are immutable: they can only be set once during\n * construction.\n */\n constructor (string memory name_, string memory symbol_) public {\n _name = name_;\n _symbol = symbol_;\n _decimals = 18;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is\n * called.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual returns (uint8) {\n return _decimals;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n return true;\n }\n\n /**\n * @dev Moves tokens `amount` from `sender` to `recipient`.\n *\n * This is internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n _balances[recipient] = _balances[recipient].add(amount);\n emit Transfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply = _totalSupply.add(amount);\n _balances[account] = _balances[account].add(amount);\n emit Transfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n _totalSupply = _totalSupply.sub(amount);\n emit Transfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(address owner, address spender, uint256 amount) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Sets {decimals} to a value other than the default one of 18.\n *\n * WARNING: This function should only be called from the constructor. Most\n * applications that interact with token contracts will not expect\n * {decimals} to ever change, and may work incorrectly if it does.\n */\n function _setupDecimals(uint8 decimals_) internal virtual {\n _decimals = decimals_;\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be to transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n}\n" diff --git a/packages/token-distribution/deployments/goerli/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json b/packages/token-distribution/deployments/goerli/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json index 4eda754ae..1bd0bdd60 100644 --- a/packages/token-distribution/deployments/goerli/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json +++ b/packages/token-distribution/deployments/goerli/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json @@ -113,7 +113,7 @@ "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { ITokenGateway } from \"../arbitrum//ITokenGateway.sol\";\nimport { GraphTokenMock } from \"./GraphTokenMock.sol\";\nimport { ICallhookReceiver } from \"../ICallhookReceiver.sol\";\n\n/**\n * @title L2 Token Gateway mock contract\n * @dev Used for testing purposes, DO NOT USE IN PRODUCTION\n */\ncontract L2TokenGatewayMock is Ownable {\n /// Address of the L1 GRT contract\n address public immutable l1Token;\n /// Address of the L2 GRT contract\n address public immutable l2Token;\n /// Next ID to return when sending an outboundTransfer\n uint256 public nextId;\n\n /// @dev Emitted when a (fake) transaction to L1 is created\n event FakeTxToL1(address from, bytes outboundCalldata);\n /// @dev Emitted when a (fake) retryable ticket is received from L1\n event DepositFinalized(address token, address indexed from, address indexed to, uint256 amount);\n\n /// @dev Emitted when an outbound transfer is initiated, i.e. tokens are withdrawn to L1 from L2\n event WithdrawalInitiated(\n address l1Token,\n address indexed from,\n address indexed to,\n uint256 indexed sequenceNumber,\n uint256 amount\n );\n\n /**\n * @notice L2 Token Gateway Contract Constructor.\n * @param _l1Token Address of the L1 GRT contract\n * @param _l2Token Address of the L2 GRT contract\n */\n constructor(address _l1Token, address _l2Token) {\n l1Token = _l1Token;\n l2Token = _l2Token;\n }\n\n /**\n * @notice Creates and sends a (fake) transfer of GRT to L1.\n * This mock will actually just emit an event with parameters equivalent to what the real L2GraphTokenGateway\n * would send to L1.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _to Recipient address on L2\n * @param _amount Amount of tokens to tranfer\n * @param _data Encoded maxSubmissionCost and sender address along with additional calldata\n * @return ID of the L2-L1 message (incrementing on every call)\n */\n function outboundTransfer(\n address _l1Token,\n address _to,\n uint256 _amount,\n uint256,\n uint256,\n bytes calldata _data\n ) external payable returns (bytes memory) {\n require(_l1Token == l1Token, \"INVALID_L1_TOKEN\");\n require(_amount > 0, \"INVALID_ZERO_AMOUNT\");\n require(_to != address(0), \"INVALID_DESTINATION\");\n\n // nested scopes to avoid stack too deep errors\n address from;\n uint256 id = nextId;\n nextId += 1;\n {\n bytes memory outboundCalldata;\n {\n bytes memory extraData;\n (from, extraData) = _parseOutboundData(_data);\n\n require(msg.value == 0, \"!value\");\n require(extraData.length == 0, \"!extraData\");\n outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData);\n }\n {\n // burn tokens from the sender, they will be released from escrow in L1\n GraphTokenMock(l2Token).bridgeBurn(from, _amount);\n\n emit FakeTxToL1(from, outboundCalldata);\n }\n }\n emit WithdrawalInitiated(_l1Token, from, _to, id, _amount);\n\n return abi.encode(id);\n }\n\n /**\n * @notice (Mock) Receives withdrawn tokens from L1\n * Implements calling callhooks if data is non-empty.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _from Address of the sender\n * @param _to Recipient address on L1\n * @param _amount Amount of tokens transferred\n * @param _data Additional calldata, will trigger an onTokenTransfer call if non-empty\n */\n function finalizeInboundTransfer(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes calldata _data\n ) external payable {\n require(_l1Token == l1Token, \"TOKEN_NOT_GRT\");\n require(msg.value == 0, \"INVALID_NONZERO_VALUE\");\n\n GraphTokenMock(l2Token).bridgeMint(_to, _amount);\n\n if (_data.length > 0) {\n ICallhookReceiver(_to).onTokenTransfer(_from, _amount, _data);\n }\n\n emit DepositFinalized(_l1Token, _from, _to, _amount);\n }\n\n /**\n * @notice Calculate the L2 address of a bridged token\n * @dev In our case, this would only work for GRT.\n * @param l1ERC20 address of L1 GRT contract\n * @return L2 address of the bridged GRT token\n */\n function calculateL2TokenAddress(address l1ERC20) public view returns (address) {\n if (l1ERC20 != l1Token) {\n return address(0);\n }\n return l2Token;\n }\n\n /**\n * @notice Creates calldata required to create a tx to L1\n * @param _l1Token Address of the Graph token contract on L1\n * @param _from Address on L2 from which we're transferring tokens\n * @param _to Address on L1 to which we're transferring tokens\n * @param _amount Amount of GRT to transfer\n * @param _data Additional call data for the L1 transaction, which must be empty\n * @return Encoded calldata (including function selector) for the L1 transaction\n */\n function getOutboundCalldata(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes memory _data\n ) public pure returns (bytes memory) {\n return\n abi.encodeWithSelector(\n ITokenGateway.finalizeInboundTransfer.selector,\n _l1Token,\n _from,\n _to,\n _amount,\n abi.encode(0, _data)\n );\n }\n\n /**\n * @dev Decodes calldata required for transfer of tokens to L1.\n * extraData can be left empty\n * @param _data Encoded callhook data\n * @return Sender of the tx\n * @return Any other data sent to L1\n */\n function _parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) {\n address from;\n bytes memory extraData;\n // The mock doesn't take messages from the Router\n from = msg.sender;\n extraData = _data;\n return (from, extraData);\n }\n}\n" }, "contracts/tests/Stakes.sol": { - "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "contracts/tests/StakingMock.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n receive() external payable {}\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" diff --git a/packages/token-distribution/deployments/mainnet/solcInputs/6f5e8f450f52dd96ebb796aa6620fee9.json b/packages/token-distribution/deployments/mainnet/solcInputs/6f5e8f450f52dd96ebb796aa6620fee9.json index fafe342a8..ff4bd37ea 100644 --- a/packages/token-distribution/deployments/mainnet/solcInputs/6f5e8f450f52dd96ebb796aa6620fee9.json +++ b/packages/token-distribution/deployments/mainnet/solcInputs/6f5e8f450f52dd96ebb796aa6620fee9.json @@ -44,7 +44,7 @@ "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" }, "contracts/Stakes.sol": { - "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(\n Stakes.Indexer storage stake,\n uint256 _tokens,\n uint256 _period\n ) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity)\n internal\n pure\n returns (uint256)\n {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(\n Stakes.Indexer storage stake,\n uint256 _tokens,\n uint256 _period\n ) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity)\n internal\n pure\n returns (uint256)\n {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "@openzeppelin/contracts/token/ERC20/ERC20.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../../utils/Context.sol\";\nimport \"./IERC20.sol\";\nimport \"../../math/SafeMath.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20 {\n using SafeMath for uint256;\n\n mapping (address => uint256) private _balances;\n\n mapping (address => mapping (address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for {name} and {symbol}, initializes {decimals} with\n * a default value of 18.\n *\n * To select a different value for {decimals}, use {_setupDecimals}.\n *\n * All three of these values are immutable: they can only be set once during\n * construction.\n */\n constructor (string memory name_, string memory symbol_) public {\n _name = name_;\n _symbol = symbol_;\n _decimals = 18;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view virtual returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view virtual returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is\n * called.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view virtual returns (uint8) {\n return _decimals;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view virtual override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view virtual override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n return true;\n }\n\n /**\n * @dev Moves tokens `amount` from `sender` to `recipient`.\n *\n * This is internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n _balances[recipient] = _balances[recipient].add(amount);\n emit Transfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply = _totalSupply.add(amount);\n _balances[account] = _balances[account].add(amount);\n emit Transfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n _totalSupply = _totalSupply.sub(amount);\n emit Transfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(address owner, address spender, uint256 amount) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Sets {decimals} to a value other than the default one of 18.\n *\n * WARNING: This function should only be called from the constructor. Most\n * applications that interact with token contracts will not expect\n * {decimals} to ever change, and may work incorrectly if it does.\n */\n function _setupDecimals(uint8 decimals_) internal virtual {\n _decimals = decimals_;\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be to transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n}\n" diff --git a/packages/token-distribution/deployments/mainnet/solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json b/packages/token-distribution/deployments/mainnet/solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json index ee7330e86..1c8c689fe 100644 --- a/packages/token-distribution/deployments/mainnet/solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json +++ b/packages/token-distribution/deployments/mainnet/solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json @@ -44,7 +44,7 @@ "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" }, "contracts/Stakes.sol": { - "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(\n Stakes.Indexer storage stake,\n uint256 _tokens,\n uint256 _period\n ) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity)\n internal\n pure\n returns (uint256)\n {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(\n Stakes.Indexer storage stake,\n uint256 _tokens,\n uint256 _period\n ) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity)\n internal\n pure\n returns (uint256)\n {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "@openzeppelin/contracts/token/ERC20/ERC20.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../../GSN/Context.sol\";\nimport \"./IERC20.sol\";\nimport \"../../math/SafeMath.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20 {\n using SafeMath for uint256;\n\n mapping (address => uint256) private _balances;\n\n mapping (address => mapping (address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for {name} and {symbol}, initializes {decimals} with\n * a default value of 18.\n *\n * To select a different value for {decimals}, use {_setupDecimals}.\n *\n * All three of these values are immutable: they can only be set once during\n * construction.\n */\n constructor (string memory name_, string memory symbol_) public {\n _name = name_;\n _symbol = symbol_;\n _decimals = 18;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is\n * called.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n return true;\n }\n\n /**\n * @dev Moves tokens `amount` from `sender` to `recipient`.\n *\n * This is internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n _balances[recipient] = _balances[recipient].add(amount);\n emit Transfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply = _totalSupply.add(amount);\n _balances[account] = _balances[account].add(amount);\n emit Transfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n _totalSupply = _totalSupply.sub(amount);\n emit Transfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(address owner, address spender, uint256 amount) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Sets {decimals} to a value other than the default one of 18.\n *\n * WARNING: This function should only be called from the constructor. Most\n * applications that interact with token contracts will not expect\n * {decimals} to ever change, and may work incorrectly if it does.\n */\n function _setupDecimals(uint8 decimals_) internal {\n _decimals = decimals_;\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be to transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n}\n" diff --git a/packages/token-distribution/deployments/mainnet/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json b/packages/token-distribution/deployments/mainnet/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json index 4eda754ae..1bd0bdd60 100644 --- a/packages/token-distribution/deployments/mainnet/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json +++ b/packages/token-distribution/deployments/mainnet/solcInputs/b5cdad58099d39cd1aed000b2fd864d8.json @@ -113,7 +113,7 @@ "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { ITokenGateway } from \"../arbitrum//ITokenGateway.sol\";\nimport { GraphTokenMock } from \"./GraphTokenMock.sol\";\nimport { ICallhookReceiver } from \"../ICallhookReceiver.sol\";\n\n/**\n * @title L2 Token Gateway mock contract\n * @dev Used for testing purposes, DO NOT USE IN PRODUCTION\n */\ncontract L2TokenGatewayMock is Ownable {\n /// Address of the L1 GRT contract\n address public immutable l1Token;\n /// Address of the L2 GRT contract\n address public immutable l2Token;\n /// Next ID to return when sending an outboundTransfer\n uint256 public nextId;\n\n /// @dev Emitted when a (fake) transaction to L1 is created\n event FakeTxToL1(address from, bytes outboundCalldata);\n /// @dev Emitted when a (fake) retryable ticket is received from L1\n event DepositFinalized(address token, address indexed from, address indexed to, uint256 amount);\n\n /// @dev Emitted when an outbound transfer is initiated, i.e. tokens are withdrawn to L1 from L2\n event WithdrawalInitiated(\n address l1Token,\n address indexed from,\n address indexed to,\n uint256 indexed sequenceNumber,\n uint256 amount\n );\n\n /**\n * @notice L2 Token Gateway Contract Constructor.\n * @param _l1Token Address of the L1 GRT contract\n * @param _l2Token Address of the L2 GRT contract\n */\n constructor(address _l1Token, address _l2Token) {\n l1Token = _l1Token;\n l2Token = _l2Token;\n }\n\n /**\n * @notice Creates and sends a (fake) transfer of GRT to L1.\n * This mock will actually just emit an event with parameters equivalent to what the real L2GraphTokenGateway\n * would send to L1.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _to Recipient address on L2\n * @param _amount Amount of tokens to tranfer\n * @param _data Encoded maxSubmissionCost and sender address along with additional calldata\n * @return ID of the L2-L1 message (incrementing on every call)\n */\n function outboundTransfer(\n address _l1Token,\n address _to,\n uint256 _amount,\n uint256,\n uint256,\n bytes calldata _data\n ) external payable returns (bytes memory) {\n require(_l1Token == l1Token, \"INVALID_L1_TOKEN\");\n require(_amount > 0, \"INVALID_ZERO_AMOUNT\");\n require(_to != address(0), \"INVALID_DESTINATION\");\n\n // nested scopes to avoid stack too deep errors\n address from;\n uint256 id = nextId;\n nextId += 1;\n {\n bytes memory outboundCalldata;\n {\n bytes memory extraData;\n (from, extraData) = _parseOutboundData(_data);\n\n require(msg.value == 0, \"!value\");\n require(extraData.length == 0, \"!extraData\");\n outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData);\n }\n {\n // burn tokens from the sender, they will be released from escrow in L1\n GraphTokenMock(l2Token).bridgeBurn(from, _amount);\n\n emit FakeTxToL1(from, outboundCalldata);\n }\n }\n emit WithdrawalInitiated(_l1Token, from, _to, id, _amount);\n\n return abi.encode(id);\n }\n\n /**\n * @notice (Mock) Receives withdrawn tokens from L1\n * Implements calling callhooks if data is non-empty.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _from Address of the sender\n * @param _to Recipient address on L1\n * @param _amount Amount of tokens transferred\n * @param _data Additional calldata, will trigger an onTokenTransfer call if non-empty\n */\n function finalizeInboundTransfer(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes calldata _data\n ) external payable {\n require(_l1Token == l1Token, \"TOKEN_NOT_GRT\");\n require(msg.value == 0, \"INVALID_NONZERO_VALUE\");\n\n GraphTokenMock(l2Token).bridgeMint(_to, _amount);\n\n if (_data.length > 0) {\n ICallhookReceiver(_to).onTokenTransfer(_from, _amount, _data);\n }\n\n emit DepositFinalized(_l1Token, _from, _to, _amount);\n }\n\n /**\n * @notice Calculate the L2 address of a bridged token\n * @dev In our case, this would only work for GRT.\n * @param l1ERC20 address of L1 GRT contract\n * @return L2 address of the bridged GRT token\n */\n function calculateL2TokenAddress(address l1ERC20) public view returns (address) {\n if (l1ERC20 != l1Token) {\n return address(0);\n }\n return l2Token;\n }\n\n /**\n * @notice Creates calldata required to create a tx to L1\n * @param _l1Token Address of the Graph token contract on L1\n * @param _from Address on L2 from which we're transferring tokens\n * @param _to Address on L1 to which we're transferring tokens\n * @param _amount Amount of GRT to transfer\n * @param _data Additional call data for the L1 transaction, which must be empty\n * @return Encoded calldata (including function selector) for the L1 transaction\n */\n function getOutboundCalldata(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes memory _data\n ) public pure returns (bytes memory) {\n return\n abi.encodeWithSelector(\n ITokenGateway.finalizeInboundTransfer.selector,\n _l1Token,\n _from,\n _to,\n _amount,\n abi.encode(0, _data)\n );\n }\n\n /**\n * @dev Decodes calldata required for transfer of tokens to L1.\n * extraData can be left empty\n * @param _data Encoded callhook data\n * @return Sender of the tx\n * @return Any other data sent to L1\n */\n function _parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) {\n address from;\n bytes memory extraData;\n // The mock doesn't take messages from the Router\n from = msg.sender;\n extraData = _data;\n return (from, extraData);\n }\n}\n" }, "contracts/tests/Stakes.sol": { - "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "contracts/tests/StakingMock.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n receive() external payable {}\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" diff --git a/packages/token-distribution/deployments/rinkeby/solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json b/packages/token-distribution/deployments/rinkeby/solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json index ee7330e86..1c8c689fe 100644 --- a/packages/token-distribution/deployments/rinkeby/solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json +++ b/packages/token-distribution/deployments/rinkeby/solcInputs/a72ab6278ade6c5c10115f7be2c555c9.json @@ -44,7 +44,7 @@ "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n" }, "contracts/Stakes.sol": { - "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(\n Stakes.Indexer storage stake,\n uint256 _tokens,\n uint256 _period\n ) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity)\n internal\n pure\n returns (uint256)\n {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "pragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(\n Stakes.Indexer storage stake,\n uint256 _tokens,\n uint256 _period\n ) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(Stakes.Indexer memory stake, uint256 _delegatedCapacity)\n internal\n pure\n returns (uint256)\n {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "@openzeppelin/contracts/token/ERC20/ERC20.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity >=0.6.0 <0.8.0;\n\nimport \"../../GSN/Context.sol\";\nimport \"./IERC20.sol\";\nimport \"../../math/SafeMath.sol\";\n\n/**\n * @dev Implementation of the {IERC20} interface.\n *\n * This implementation is agnostic to the way tokens are created. This means\n * that a supply mechanism has to be added in a derived contract using {_mint}.\n * For a generic mechanism see {ERC20PresetMinterPauser}.\n *\n * TIP: For a detailed writeup see our guide\n * https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How\n * to implement supply mechanisms].\n *\n * We have followed general OpenZeppelin guidelines: functions revert instead\n * of returning `false` on failure. This behavior is nonetheless conventional\n * and does not conflict with the expectations of ERC20 applications.\n *\n * Additionally, an {Approval} event is emitted on calls to {transferFrom}.\n * This allows applications to reconstruct the allowance for all accounts just\n * by listening to said events. Other implementations of the EIP may not emit\n * these events, as it isn't required by the specification.\n *\n * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}\n * functions have been added to mitigate the well-known issues around setting\n * allowances. See {IERC20-approve}.\n */\ncontract ERC20 is Context, IERC20 {\n using SafeMath for uint256;\n\n mapping (address => uint256) private _balances;\n\n mapping (address => mapping (address => uint256)) private _allowances;\n\n uint256 private _totalSupply;\n\n string private _name;\n string private _symbol;\n uint8 private _decimals;\n\n /**\n * @dev Sets the values for {name} and {symbol}, initializes {decimals} with\n * a default value of 18.\n *\n * To select a different value for {decimals}, use {_setupDecimals}.\n *\n * All three of these values are immutable: they can only be set once during\n * construction.\n */\n constructor (string memory name_, string memory symbol_) public {\n _name = name_;\n _symbol = symbol_;\n _decimals = 18;\n }\n\n /**\n * @dev Returns the name of the token.\n */\n function name() public view returns (string memory) {\n return _name;\n }\n\n /**\n * @dev Returns the symbol of the token, usually a shorter version of the\n * name.\n */\n function symbol() public view returns (string memory) {\n return _symbol;\n }\n\n /**\n * @dev Returns the number of decimals used to get its user representation.\n * For example, if `decimals` equals `2`, a balance of `505` tokens should\n * be displayed to a user as `5,05` (`505 / 10 ** 2`).\n *\n * Tokens usually opt for a value of 18, imitating the relationship between\n * Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is\n * called.\n *\n * NOTE: This information is only used for _display_ purposes: it in\n * no way affects any of the arithmetic of the contract, including\n * {IERC20-balanceOf} and {IERC20-transfer}.\n */\n function decimals() public view returns (uint8) {\n return _decimals;\n }\n\n /**\n * @dev See {IERC20-totalSupply}.\n */\n function totalSupply() public view override returns (uint256) {\n return _totalSupply;\n }\n\n /**\n * @dev See {IERC20-balanceOf}.\n */\n function balanceOf(address account) public view override returns (uint256) {\n return _balances[account];\n }\n\n /**\n * @dev See {IERC20-transfer}.\n *\n * Requirements:\n *\n * - `recipient` cannot be the zero address.\n * - the caller must have a balance of at least `amount`.\n */\n function transfer(address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(_msgSender(), recipient, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-allowance}.\n */\n function allowance(address owner, address spender) public view virtual override returns (uint256) {\n return _allowances[owner][spender];\n }\n\n /**\n * @dev See {IERC20-approve}.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function approve(address spender, uint256 amount) public virtual override returns (bool) {\n _approve(_msgSender(), spender, amount);\n return true;\n }\n\n /**\n * @dev See {IERC20-transferFrom}.\n *\n * Emits an {Approval} event indicating the updated allowance. This is not\n * required by the EIP. See the note at the beginning of {ERC20}.\n *\n * Requirements:\n *\n * - `sender` and `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n * - the caller must have allowance for ``sender``'s tokens of at least\n * `amount`.\n */\n function transferFrom(address sender, address recipient, uint256 amount) public virtual override returns (bool) {\n _transfer(sender, recipient, amount);\n _approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, \"ERC20: transfer amount exceeds allowance\"));\n return true;\n }\n\n /**\n * @dev Atomically increases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n */\n function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));\n return true;\n }\n\n /**\n * @dev Atomically decreases the allowance granted to `spender` by the caller.\n *\n * This is an alternative to {approve} that can be used as a mitigation for\n * problems described in {IERC20-approve}.\n *\n * Emits an {Approval} event indicating the updated allowance.\n *\n * Requirements:\n *\n * - `spender` cannot be the zero address.\n * - `spender` must have allowance for the caller of at least\n * `subtractedValue`.\n */\n function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {\n _approve(_msgSender(), spender, _allowances[_msgSender()][spender].sub(subtractedValue, \"ERC20: decreased allowance below zero\"));\n return true;\n }\n\n /**\n * @dev Moves tokens `amount` from `sender` to `recipient`.\n *\n * This is internal function is equivalent to {transfer}, and can be used to\n * e.g. implement automatic token fees, slashing mechanisms, etc.\n *\n * Emits a {Transfer} event.\n *\n * Requirements:\n *\n * - `sender` cannot be the zero address.\n * - `recipient` cannot be the zero address.\n * - `sender` must have a balance of at least `amount`.\n */\n function _transfer(address sender, address recipient, uint256 amount) internal virtual {\n require(sender != address(0), \"ERC20: transfer from the zero address\");\n require(recipient != address(0), \"ERC20: transfer to the zero address\");\n\n _beforeTokenTransfer(sender, recipient, amount);\n\n _balances[sender] = _balances[sender].sub(amount, \"ERC20: transfer amount exceeds balance\");\n _balances[recipient] = _balances[recipient].add(amount);\n emit Transfer(sender, recipient, amount);\n }\n\n /** @dev Creates `amount` tokens and assigns them to `account`, increasing\n * the total supply.\n *\n * Emits a {Transfer} event with `from` set to the zero address.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n */\n function _mint(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: mint to the zero address\");\n\n _beforeTokenTransfer(address(0), account, amount);\n\n _totalSupply = _totalSupply.add(amount);\n _balances[account] = _balances[account].add(amount);\n emit Transfer(address(0), account, amount);\n }\n\n /**\n * @dev Destroys `amount` tokens from `account`, reducing the\n * total supply.\n *\n * Emits a {Transfer} event with `to` set to the zero address.\n *\n * Requirements:\n *\n * - `account` cannot be the zero address.\n * - `account` must have at least `amount` tokens.\n */\n function _burn(address account, uint256 amount) internal virtual {\n require(account != address(0), \"ERC20: burn from the zero address\");\n\n _beforeTokenTransfer(account, address(0), amount);\n\n _balances[account] = _balances[account].sub(amount, \"ERC20: burn amount exceeds balance\");\n _totalSupply = _totalSupply.sub(amount);\n emit Transfer(account, address(0), amount);\n }\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.\n *\n * This internal function is equivalent to `approve`, and can be used to\n * e.g. set automatic allowances for certain subsystems, etc.\n *\n * Emits an {Approval} event.\n *\n * Requirements:\n *\n * - `owner` cannot be the zero address.\n * - `spender` cannot be the zero address.\n */\n function _approve(address owner, address spender, uint256 amount) internal virtual {\n require(owner != address(0), \"ERC20: approve from the zero address\");\n require(spender != address(0), \"ERC20: approve to the zero address\");\n\n _allowances[owner][spender] = amount;\n emit Approval(owner, spender, amount);\n }\n\n /**\n * @dev Sets {decimals} to a value other than the default one of 18.\n *\n * WARNING: This function should only be called from the constructor. Most\n * applications that interact with token contracts will not expect\n * {decimals} to ever change, and may work incorrectly if it does.\n */\n function _setupDecimals(uint8 decimals_) internal {\n _decimals = decimals_;\n }\n\n /**\n * @dev Hook that is called before any transfer of tokens. This includes\n * minting and burning.\n *\n * Calling conditions:\n *\n * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens\n * will be to transferred to `to`.\n * - when `from` is zero, `amount` tokens will be minted for `to`.\n * - when `to` is zero, `amount` of ``from``'s tokens will be burned.\n * - `from` and `to` are never both zero.\n *\n * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].\n */\n function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }\n}\n" diff --git a/packages/token-distribution/deployments/sepolia/solcInputs/095bd30babc75057be19228ca1fd7aa4.json b/packages/token-distribution/deployments/sepolia/solcInputs/095bd30babc75057be19228ca1fd7aa4.json index e7a4b87c0..1adcf2491 100644 --- a/packages/token-distribution/deployments/sepolia/solcInputs/095bd30babc75057be19228ca1fd7aa4.json +++ b/packages/token-distribution/deployments/sepolia/solcInputs/095bd30babc75057be19228ca1fd7aa4.json @@ -113,7 +113,7 @@ "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\n\nimport { Ownable } from \"@openzeppelin/contracts/access/Ownable.sol\";\nimport { ITokenGateway } from \"../arbitrum//ITokenGateway.sol\";\nimport { GraphTokenMock } from \"./GraphTokenMock.sol\";\nimport { ICallhookReceiver } from \"../ICallhookReceiver.sol\";\n\n/**\n * @title L2 Token Gateway mock contract\n * @dev Used for testing purposes, DO NOT USE IN PRODUCTION\n */\ncontract L2TokenGatewayMock is Ownable {\n /// Address of the L1 GRT contract\n address public immutable l1Token;\n /// Address of the L2 GRT contract\n address public immutable l2Token;\n /// Next ID to return when sending an outboundTransfer\n uint256 public nextId;\n\n /// @dev Emitted when a (fake) transaction to L1 is created\n event FakeTxToL1(address from, bytes outboundCalldata);\n /// @dev Emitted when a (fake) retryable ticket is received from L1\n event DepositFinalized(address token, address indexed from, address indexed to, uint256 amount);\n\n /// @dev Emitted when an outbound transfer is initiated, i.e. tokens are withdrawn to L1 from L2\n event WithdrawalInitiated(\n address l1Token,\n address indexed from,\n address indexed to,\n uint256 indexed sequenceNumber,\n uint256 amount\n );\n\n /**\n * @notice L2 Token Gateway Contract Constructor.\n * @param _l1Token Address of the L1 GRT contract\n * @param _l2Token Address of the L2 GRT contract\n */\n constructor(address _l1Token, address _l2Token) {\n l1Token = _l1Token;\n l2Token = _l2Token;\n }\n\n /**\n * @notice Creates and sends a (fake) transfer of GRT to L1.\n * This mock will actually just emit an event with parameters equivalent to what the real L2GraphTokenGateway\n * would send to L1.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _to Recipient address on L2\n * @param _amount Amount of tokens to tranfer\n * @param _data Encoded maxSubmissionCost and sender address along with additional calldata\n * @return ID of the L2-L1 message (incrementing on every call)\n */\n function outboundTransfer(\n address _l1Token,\n address _to,\n uint256 _amount,\n uint256,\n uint256,\n bytes calldata _data\n ) external payable returns (bytes memory) {\n require(_l1Token == l1Token, \"INVALID_L1_TOKEN\");\n require(_amount > 0, \"INVALID_ZERO_AMOUNT\");\n require(_to != address(0), \"INVALID_DESTINATION\");\n\n // nested scopes to avoid stack too deep errors\n address from;\n uint256 id = nextId;\n nextId += 1;\n {\n bytes memory outboundCalldata;\n {\n bytes memory extraData;\n (from, extraData) = _parseOutboundData(_data);\n\n require(msg.value == 0, \"!value\");\n require(extraData.length == 0, \"!extraData\");\n outboundCalldata = getOutboundCalldata(_l1Token, from, _to, _amount, extraData);\n }\n {\n // burn tokens from the sender, they will be released from escrow in L1\n GraphTokenMock(l2Token).bridgeBurn(from, _amount);\n\n emit FakeTxToL1(from, outboundCalldata);\n }\n }\n emit WithdrawalInitiated(_l1Token, from, _to, id, _amount);\n\n return abi.encode(id);\n }\n\n /**\n * @notice (Mock) Receives withdrawn tokens from L1\n * Implements calling callhooks if data is non-empty.\n * @param _l1Token L1 Address of the GRT contract (needed for compatibility with Arbitrum Gateway Router)\n * @param _from Address of the sender\n * @param _to Recipient address on L1\n * @param _amount Amount of tokens transferred\n * @param _data Additional calldata, will trigger an onTokenTransfer call if non-empty\n */\n function finalizeInboundTransfer(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes calldata _data\n ) external payable {\n require(_l1Token == l1Token, \"TOKEN_NOT_GRT\");\n require(msg.value == 0, \"INVALID_NONZERO_VALUE\");\n\n GraphTokenMock(l2Token).bridgeMint(_to, _amount);\n\n if (_data.length > 0) {\n ICallhookReceiver(_to).onTokenTransfer(_from, _amount, _data);\n }\n\n emit DepositFinalized(_l1Token, _from, _to, _amount);\n }\n\n /**\n * @notice Calculate the L2 address of a bridged token\n * @dev In our case, this would only work for GRT.\n * @param l1ERC20 address of L1 GRT contract\n * @return L2 address of the bridged GRT token\n */\n function calculateL2TokenAddress(address l1ERC20) public view returns (address) {\n if (l1ERC20 != l1Token) {\n return address(0);\n }\n return l2Token;\n }\n\n /**\n * @notice Creates calldata required to create a tx to L1\n * @param _l1Token Address of the Graph token contract on L1\n * @param _from Address on L2 from which we're transferring tokens\n * @param _to Address on L1 to which we're transferring tokens\n * @param _amount Amount of GRT to transfer\n * @param _data Additional call data for the L1 transaction, which must be empty\n * @return Encoded calldata (including function selector) for the L1 transaction\n */\n function getOutboundCalldata(\n address _l1Token,\n address _from,\n address _to,\n uint256 _amount,\n bytes memory _data\n ) public pure returns (bytes memory) {\n return\n abi.encodeWithSelector(\n ITokenGateway.finalizeInboundTransfer.selector,\n _l1Token,\n _from,\n _to,\n _amount,\n abi.encode(0, _data)\n );\n }\n\n /**\n * @dev Decodes calldata required for transfer of tokens to L1.\n * extraData can be left empty\n * @param _data Encoded callhook data\n * @return Sender of the tx\n * @return Any other data sent to L1\n */\n function _parseOutboundData(bytes calldata _data) private view returns (address, bytes memory) {\n address from;\n bytes memory extraData;\n // The mock doesn't take messages from the Router\n from = msg.sender;\n extraData = _data;\n return (from, extraData);\n }\n}\n" }, "contracts/tests/Stakes.sol": { - "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unkock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" + "content": "// SPDX-License-Identifier: GPL-2.0-or-later\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/math/SafeMath.sol\";\n\n/**\n * @title A collection of data structures and functions to manage the Indexer Stake state.\n * Used for low-level state changes, require() conditions should be evaluated\n * at the caller function scope.\n */\nlibrary Stakes {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n struct Indexer {\n uint256 tokensStaked; // Tokens on the indexer stake (staked by the indexer)\n uint256 tokensAllocated; // Tokens used in allocations\n uint256 tokensLocked; // Tokens locked for withdrawal subject to thawing period\n uint256 tokensLockedUntil; // Block when locked tokens can be withdrawn\n }\n\n /**\n * @dev Deposit tokens to the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to deposit\n */\n function deposit(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.add(_tokens);\n }\n\n /**\n * @dev Release tokens from the indexer stake.\n * @param stake Stake data\n * @param _tokens Amount of tokens to release\n */\n function release(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensStaked = stake.tokensStaked.sub(_tokens);\n }\n\n /**\n * @dev Allocate tokens from the main stack to a SubgraphDeployment.\n * @param stake Stake data\n * @param _tokens Amount of tokens to allocate\n */\n function allocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.add(_tokens);\n }\n\n /**\n * @dev Unallocate tokens from a SubgraphDeployment back to the main stack.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unallocate\n */\n function unallocate(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensAllocated = stake.tokensAllocated.sub(_tokens);\n }\n\n /**\n * @dev Lock tokens until a thawing period pass.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _period Period in blocks that need to pass before withdrawal\n */\n function lockTokens(Stakes.Indexer storage stake, uint256 _tokens, uint256 _period) internal {\n // Take into account period averaging for multiple unstake requests\n uint256 lockingPeriod = _period;\n if (stake.tokensLocked > 0) {\n lockingPeriod = stake.getLockingPeriod(_tokens, _period);\n }\n\n // Update balances\n stake.tokensLocked = stake.tokensLocked.add(_tokens);\n stake.tokensLockedUntil = block.number.add(lockingPeriod);\n }\n\n /**\n * @dev Unlock tokens.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unlock\n */\n function unlockTokens(Stakes.Indexer storage stake, uint256 _tokens) internal {\n stake.tokensLocked = stake.tokensLocked.sub(_tokens);\n if (stake.tokensLocked == 0) {\n stake.tokensLockedUntil = 0;\n }\n }\n\n /**\n * @dev Take all tokens out from the locked stake for withdrawal.\n * @param stake Stake data\n * @return Amount of tokens being withdrawn\n */\n function withdrawTokens(Stakes.Indexer storage stake) internal returns (uint256) {\n // Calculate tokens that can be released\n uint256 tokensToWithdraw = stake.tokensWithdrawable();\n\n if (tokensToWithdraw > 0) {\n // Reset locked tokens\n stake.unlockTokens(tokensToWithdraw);\n\n // Decrease indexer stake\n stake.release(tokensToWithdraw);\n }\n\n return tokensToWithdraw;\n }\n\n /**\n * @dev Get the locking period of the tokens to unstake.\n * If already unstaked before calculate the weighted average.\n * @param stake Stake data\n * @param _tokens Amount of tokens to unstake\n * @param _thawingPeriod Period in blocks that need to pass before withdrawal\n * @return True if staked\n */\n function getLockingPeriod(\n Stakes.Indexer memory stake,\n uint256 _tokens,\n uint256 _thawingPeriod\n ) internal view returns (uint256) {\n uint256 blockNum = block.number;\n uint256 periodA = (stake.tokensLockedUntil > blockNum) ? stake.tokensLockedUntil.sub(blockNum) : 0;\n uint256 periodB = _thawingPeriod;\n uint256 stakeA = stake.tokensLocked;\n uint256 stakeB = _tokens;\n return periodA.mul(stakeA).add(periodB.mul(stakeB)).div(stakeA.add(stakeB));\n }\n\n /**\n * @dev Return true if there are tokens staked by the Indexer.\n * @param stake Stake data\n * @return True if staked\n */\n function hasTokens(Stakes.Indexer memory stake) internal pure returns (bool) {\n return stake.tokensStaked > 0;\n }\n\n /**\n * @dev Return the amount of tokens used in allocations and locked for withdrawal.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensUsed(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAllocated.add(stake.tokensLocked);\n }\n\n /**\n * @dev Return the amount of tokens staked not considering the ones that are already going\n * through the thawing period or are ready for withdrawal. We call it secure stake because\n * it is not subject to change by a withdraw call from the indexer.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensSecureStake(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensStaked.sub(stake.tokensLocked);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for any purpose.\n * Any token that is allocated cannot be used as well as tokens that are going through the\n * thawing period or are withdrawable\n * Calc: tokensStaked - tokensAllocated - tokensLocked\n * @param stake Stake data\n * @return Token amount\n */\n function tokensAvailable(Stakes.Indexer memory stake) internal pure returns (uint256) {\n return stake.tokensAvailableWithDelegation(0);\n }\n\n /**\n * @dev Tokens free balance on the indexer stake that can be used for allocations.\n * This function accepts a parameter for extra delegated capacity that takes into\n * account delegated tokens\n * @param stake Stake data\n * @param _delegatedCapacity Amount of tokens used from delegators to calculate availability\n * @return Token amount\n */\n function tokensAvailableWithDelegation(\n Stakes.Indexer memory stake,\n uint256 _delegatedCapacity\n ) internal pure returns (uint256) {\n uint256 tokensCapacity = stake.tokensStaked.add(_delegatedCapacity);\n uint256 _tokensUsed = stake.tokensUsed();\n // If more tokens are used than the current capacity, the indexer is overallocated.\n // This means the indexer doesn't have available capacity to create new allocations.\n // We can reach this state when the indexer has funds allocated and then any\n // of these conditions happen:\n // - The delegationCapacity ratio is reduced.\n // - The indexer stake is slashed.\n // - A delegator removes enough stake.\n if (_tokensUsed > tokensCapacity) {\n // Indexer stake is over allocated: return 0 to avoid stake to be used until\n // the overallocation is restored by staking more tokens, unallocating tokens\n // or using more delegated funds\n return 0;\n }\n return tokensCapacity.sub(_tokensUsed);\n }\n\n /**\n * @dev Tokens available for withdrawal after thawing period.\n * @param stake Stake data\n * @return Token amount\n */\n function tokensWithdrawable(Stakes.Indexer memory stake) internal view returns (uint256) {\n // No tokens to withdraw before locking period\n if (stake.tokensLockedUntil == 0 || block.number < stake.tokensLockedUntil) {\n return 0;\n }\n return stake.tokensLocked;\n }\n}\n" }, "contracts/tests/StakingMock.sol": { "content": "// SPDX-License-Identifier: MIT\n\npragma solidity ^0.7.3;\npragma experimental ABIEncoderV2;\n\nimport \"@openzeppelin/contracts/token/ERC20/IERC20.sol\";\n\nimport \"./Stakes.sol\";\n\ncontract StakingMock {\n using SafeMath for uint256;\n using Stakes for Stakes.Indexer;\n\n // -- State --\n\n uint256 public minimumIndexerStake = 100e18;\n uint256 public thawingPeriod = 10; // 10 blocks\n IERC20 public token;\n\n // Indexer stakes : indexer => Stake\n mapping(address => Stakes.Indexer) public stakes;\n\n /**\n * @dev Emitted when `indexer` stake `tokens` amount.\n */\n event StakeDeposited(address indexed indexer, uint256 tokens);\n\n /**\n * @dev Emitted when `indexer` unstaked and locked `tokens` amount `until` block.\n */\n event StakeLocked(address indexed indexer, uint256 tokens, uint256 until);\n\n /**\n * @dev Emitted when `indexer` withdrew `tokens` staked.\n */\n event StakeWithdrawn(address indexed indexer, uint256 tokens);\n\n // Contract constructor.\n constructor(IERC20 _token) {\n require(address(_token) != address(0), \"!token\");\n token = _token;\n }\n\n receive() external payable {}\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _tokens Amount of tokens to stake\n */\n function stake(uint256 _tokens) external {\n stakeTo(msg.sender, _tokens);\n }\n\n /**\n * @dev Deposit tokens on the indexer stake.\n * @param _indexer Address of the indexer\n * @param _tokens Amount of tokens to stake\n */\n function stakeTo(address _indexer, uint256 _tokens) public {\n require(_tokens > 0, \"!tokens\");\n\n // Ensure minimum stake\n require(stakes[_indexer].tokensSecureStake().add(_tokens) >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Transfer tokens to stake from caller to this contract\n require(token.transferFrom(msg.sender, address(this), _tokens), \"!transfer\");\n\n // Stake the transferred tokens\n _stake(_indexer, _tokens);\n }\n\n /**\n * @dev Unstake tokens from the indexer stake, lock them until thawing period expires.\n * @param _tokens Amount of tokens to unstake\n */\n function unstake(uint256 _tokens) external {\n address indexer = msg.sender;\n Stakes.Indexer storage indexerStake = stakes[indexer];\n\n require(_tokens > 0, \"!tokens\");\n require(indexerStake.hasTokens(), \"!stake\");\n require(indexerStake.tokensAvailable() >= _tokens, \"!stake-avail\");\n\n // Ensure minimum stake\n uint256 newStake = indexerStake.tokensSecureStake().sub(_tokens);\n require(newStake == 0 || newStake >= minimumIndexerStake, \"!minimumIndexerStake\");\n\n // Before locking more tokens, withdraw any unlocked ones\n uint256 tokensToWithdraw = indexerStake.tokensWithdrawable();\n if (tokensToWithdraw > 0) {\n _withdraw(indexer);\n }\n\n indexerStake.lockTokens(_tokens, thawingPeriod);\n\n emit StakeLocked(indexer, indexerStake.tokensLocked, indexerStake.tokensLockedUntil);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n */\n function withdraw() external {\n _withdraw(msg.sender);\n }\n\n function _stake(address _indexer, uint256 _tokens) internal {\n // Deposit tokens into the indexer stake\n Stakes.Indexer storage indexerStake = stakes[_indexer];\n indexerStake.deposit(_tokens);\n\n emit StakeDeposited(_indexer, _tokens);\n }\n\n /**\n * @dev Withdraw indexer tokens once the thawing period has passed.\n * @param _indexer Address of indexer to withdraw funds from\n */\n function _withdraw(address _indexer) private {\n // Get tokens available for withdraw and update balance\n uint256 tokensToWithdraw = stakes[_indexer].withdrawTokens();\n require(tokensToWithdraw > 0, \"!tokens\");\n\n // Return tokens to the indexer\n require(token.transfer(_indexer, tokensToWithdraw), \"!transfer\");\n\n emit StakeWithdrawn(_indexer, tokensToWithdraw);\n }\n}\n"