From 2cfed8afdbd8768ec9633cf07c1d105c9b104e98 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 2 Nov 2023 13:45:17 -0300 Subject: [PATCH 1/4] fix: set and hardcode minimum L2 curation to 1 GRT, enforce it in all cases, allow sub-epoch collect --- contracts/curation/Curation.sol | 16 +++-- contracts/curation/CurationStorage.sol | 7 ++- contracts/l2/curation/L2Curation.sol | 68 ++++++++++----------- contracts/l2/discovery/L2GNS.sol | 42 ++++++++----- contracts/staking/Staking.sol | 8 +-- test/curation/configuration.test.ts | 4 ++ test/l2/l2Curation.test.ts | 85 +++++--------------------- test/lib/deployment.ts | 2 +- test/staking/allocation.test.ts | 5 -- 9 files changed, 97 insertions(+), 140 deletions(-) diff --git a/contracts/curation/Curation.sol b/contracts/curation/Curation.sol index 04c3a6217..d69d940fa 100644 --- a/contracts/curation/Curation.sol +++ b/contracts/curation/Curation.sol @@ -362,16 +362,16 @@ contract Curation is CurationV2Storage, GraphUpgradeable { // Init curation pool if (curationPool.tokens == 0) { require( - _tokensIn >= minimumCurationDeposit, + _tokensIn >= __minimumCurationDeposit, "Curation deposit is below minimum required" ); return BancorFormula(bondingCurve) .calculatePurchaseReturn( SIGNAL_PER_MINIMUM_DEPOSIT, - minimumCurationDeposit, + __minimumCurationDeposit, defaultReserveRatio, - _tokensIn.sub(minimumCurationDeposit) + _tokensIn.sub(__minimumCurationDeposit) ) .add(SIGNAL_PER_MINIMUM_DEPOSIT); } @@ -417,6 +417,14 @@ contract Curation is CurationV2Storage, GraphUpgradeable { ); } + /** + * @notice Getter for minimum curation deposit + * @return Minimum amount of tokens that must be deposited to mint signal + */ + function minimumCurationDeposit() external view returns (uint256) { + return __minimumCurationDeposit; + } + /** * @dev Internal: Set the default reserve ratio percentage for a curation pool. * @notice Update the default reserver ratio to `_defaultReserveRatio` @@ -442,7 +450,7 @@ contract Curation is CurationV2Storage, GraphUpgradeable { function _setMinimumCurationDeposit(uint256 _minimumCurationDeposit) private { require(_minimumCurationDeposit != 0, "Minimum curation deposit cannot be 0"); - minimumCurationDeposit = _minimumCurationDeposit; + __minimumCurationDeposit = _minimumCurationDeposit; emit ParameterUpdated("minimumCurationDeposit"); } diff --git a/contracts/curation/CurationStorage.sol b/contracts/curation/CurationStorage.sol index bcbf0df6b..0b726ec89 100644 --- a/contracts/curation/CurationStorage.sol +++ b/contracts/curation/CurationStorage.sol @@ -43,9 +43,10 @@ abstract contract CurationV1Storage is Managed, ICuration { /// @dev This is used as the target for GraphCurationToken clones. address public curationTokenMaster; - /// Minimum amount allowed to be deposited by curators to initialize a pool - /// @dev This is the `startPoolBalance` for the bonding curve - uint256 public minimumCurationDeposit; + /// @dev Minimum amount allowed to be deposited by curators to initialize a pool + /// In L2Curation, this is now replaced by the MINIMUM_CURATION_DEPOSIT constant. + /// So this is now only used in Curation in L1. + uint256 internal __minimumCurationDeposit; /// Bonding curve library /// Unused in L2. diff --git a/contracts/l2/curation/L2Curation.sol b/contracts/l2/curation/L2Curation.sol index 6e32f2112..fbf19ba2c 100644 --- a/contracts/l2/curation/L2Curation.sol +++ b/contracts/l2/curation/L2Curation.sol @@ -34,8 +34,11 @@ contract L2Curation is CurationV2Storage, GraphUpgradeable, IL2Curation { /// @dev 100% in parts per million uint32 private constant MAX_PPM = 1000000; + /// @dev Minimum amount of tokens that must be deposited to mint signal + uint256 private constant MINIMUM_CURATION_DEPOSIT = 1e18; + /// @dev Amount of signal you get with your minimum token deposit - uint256 private constant SIGNAL_PER_MINIMUM_DEPOSIT = 1; // 1e-18 signal as 18 decimal number + uint256 private constant SIGNAL_PER_MINIMUM_DEPOSIT = 1e18; /// @dev Reserve ratio for all subgraphs set to 100% for a flat bonding curve uint32 private immutable fixedReserveRatio = MAX_PPM; @@ -85,13 +88,12 @@ contract L2Curation is CurationV2Storage, GraphUpgradeable, IL2Curation { * @param _controller Controller contract that manages this contract * @param _curationTokenMaster Address of the GraphCurationToken master copy * @param _curationTaxPercentage Percentage of curation tax to be collected - * @param _minimumCurationDeposit Minimum amount of tokens that can be deposited as curation signal */ function initialize( address _controller, address _curationTokenMaster, uint32 _curationTaxPercentage, - uint256 _minimumCurationDeposit + uint256 // _minimumCurationDeposit, not used in L2 ) external onlyImpl initializer { Managed._initialize(_controller); @@ -99,7 +101,6 @@ contract L2Curation is CurationV2Storage, GraphUpgradeable, IL2Curation { defaultReserveRatio = fixedReserveRatio; emit ParameterUpdated("defaultReserveRatio"); _setCurationTaxPercentage(_curationTaxPercentage); - _setMinimumCurationDeposit(_minimumCurationDeposit); _setCurationTokenMaster(_curationTokenMaster); } @@ -111,19 +112,6 @@ contract L2Curation is CurationV2Storage, GraphUpgradeable, IL2Curation { revert("Not implemented in L2"); } - /** - * @dev Set the minimum deposit amount for curators. - * @notice Update the minimum deposit amount to `_minimumCurationDeposit` - * @param _minimumCurationDeposit Minimum amount of tokens required deposit - */ - function setMinimumCurationDeposit(uint256 _minimumCurationDeposit) - external - override - onlyGovernor - { - _setMinimumCurationDeposit(_minimumCurationDeposit); - } - /** * @notice Set the curation tax percentage to charge when a curator deposits GRT tokens. * @param _percentage Curation tax percentage charged when depositing GRT tokens @@ -296,17 +284,22 @@ contract L2Curation is CurationV2Storage, GraphUpgradeable, IL2Curation { uint256 _tokensOutMin ) external override notPartialPaused returns (uint256) { address curator = msg.sender; - + uint256 previousCuratorSignal = getCuratorSignal(curator, _subgraphDeploymentID); // Validations require(_signalIn != 0, "Cannot burn zero signal"); - require( - getCuratorSignal(curator, _subgraphDeploymentID) >= _signalIn, - "Cannot burn more signal than you own" - ); + require(previousCuratorSignal >= _signalIn, "Cannot burn more signal than you own"); // Get the amount of tokens to refund based on returned signal uint256 tokensOut = signalToTokens(_subgraphDeploymentID, _signalIn); - + if (previousCuratorSignal > _signalIn) { + // The user's remaining tokens can't be under the minimum curation, + // to prevent rounding attacks + uint256 remainingTokens = signalToTokens( + _subgraphDeploymentID, + previousCuratorSignal - _signalIn + ); + require(remainingTokens >= MINIMUM_CURATION_DEPOSIT, "Less than minimum curation left"); + } // Slippage protection require(tokensOut >= _tokensOutMin, "Slippage protection"); @@ -448,15 +441,18 @@ contract L2Curation is CurationV2Storage, GraphUpgradeable, IL2Curation { } /** - * @dev Internal: Set the minimum deposit amount for curators. - * Update the minimum deposit amount to `_minimumCurationDeposit` - * @param _minimumCurationDeposit Minimum amount of tokens required deposit + * @notice (Deprecated in L2) Update the minimum deposit amount to `_minimumCurationDeposit` */ - function _setMinimumCurationDeposit(uint256 _minimumCurationDeposit) private { - require(_minimumCurationDeposit != 0, "Minimum curation deposit cannot be 0"); + function setMinimumCurationDeposit(uint256) external view override onlyGovernor { + revert("Not implemented in L2"); + } - minimumCurationDeposit = _minimumCurationDeposit; - emit ParameterUpdated("minimumCurationDeposit"); + /** + * @notice Getter for minimum curation deposit + * @return Minimum amount of tokens that must be deposited to mint signal + */ + function minimumCurationDeposit() external view returns (uint256) { + return MINIMUM_CURATION_DEPOSIT; } /** @@ -510,19 +506,19 @@ contract L2Curation is CurationV2Storage, GraphUpgradeable, IL2Curation { view returns (uint256) { + require( + _tokensIn >= MINIMUM_CURATION_DEPOSIT, + "Curation deposit is below minimum required" + ); // Get curation pool tokens and signal CurationPool memory curationPool = pools[_subgraphDeploymentID]; // Init curation pool if (curationPool.tokens == 0) { - require( - _tokensIn >= minimumCurationDeposit, - "Curation deposit is below minimum required" - ); return SIGNAL_PER_MINIMUM_DEPOSIT.add( - SIGNAL_PER_MINIMUM_DEPOSIT.mul(_tokensIn.sub(minimumCurationDeposit)).div( - minimumCurationDeposit + SIGNAL_PER_MINIMUM_DEPOSIT.mul(_tokensIn.sub(MINIMUM_CURATION_DEPOSIT)).div( + MINIMUM_CURATION_DEPOSIT ) ); } diff --git a/contracts/l2/discovery/L2GNS.sol b/contracts/l2/discovery/L2GNS.sol index c8d952135..b799307b6 100644 --- a/contracts/l2/discovery/L2GNS.sol +++ b/contracts/l2/discovery/L2GNS.sol @@ -26,9 +26,13 @@ import { IL2Curation } from "../curation/IL2Curation.sol"; contract L2GNS is GNS, L2GNSV1Storage, IL2GNS { using SafeMathUpgradeable for uint256; + /// Offset added to subgraph IDs to calculate the L2 alias of L1 subgraph IDs uint256 public constant SUBGRAPH_ID_ALIAS_OFFSET = uint256(0x1111000000000000000000000000000000000000000000000000000000001111); + /// @dev Minimum curation deposit, must match the value in L2Curation + uint256 private constant MINIMUM_CURATION_DEPOSIT = 1e18; + /// @dev Emitted when a subgraph is received from L1 through the bridge event SubgraphReceivedFromL1( uint256 indexed _l1SubgraphID, @@ -46,7 +50,8 @@ contract L2GNS is GNS, L2GNSV1Storage, IL2GNS { uint256 _tokens ); /// @dev Emitted when the L1 balance for a curator has been returned to the beneficiary. - /// This can happen if the subgraph transfer was not finished when the curator's tokens arrived. + /// This can happen if the subgraph transfer was not finished when the curator's tokens arrived, + /// or if the tokens were under the L2 minimum curation deposit event CuratorBalanceReturnedToBeneficiary( uint256 indexed _l1SubgraphID, address indexed _l2Curator, @@ -124,26 +129,27 @@ contract L2GNS is GNS, L2GNSV1Storage, IL2GNS { IL2Curation curation = IL2Curation(address(curation())); // Update pool: constant nSignal, vSignal can change (w/no slippage protection) // Buy all signal from the new deployment - uint256 vSignal = curation.mintTaxFree(_subgraphDeploymentID, transferData.tokens); - uint256 nSignal = vSignalToNSignal(_l2SubgraphID, vSignal); + uint256 vSignal; + uint256 nSignal; + if (transferData.tokens >= MINIMUM_CURATION_DEPOSIT) { + // Update pool: constant nSignal, vSignal can change (w/no slippage protection) + // Buy all signal from the new deployment + vSignal = curation.mintTaxFree(_subgraphDeploymentID, transferData.tokens); + nSignal = vSignalToNSignal(_l2SubgraphID, vSignal); + subgraphData.vSignal = vSignal; + subgraphData.nSignal = nSignal; + subgraphData.curatorNSignal[msg.sender] = nSignal; + emit SignalMinted(_l2SubgraphID, msg.sender, nSignal, vSignal, transferData.tokens); + } subgraphData.disabled = false; - subgraphData.vSignal = vSignal; - subgraphData.nSignal = nSignal; - subgraphData.curatorNSignal[msg.sender] = nSignal; subgraphData.subgraphDeploymentID = _subgraphDeploymentID; // Set the token metadata _setSubgraphMetadata(_l2SubgraphID, _subgraphMetadata); emit SubgraphPublished(_l2SubgraphID, _subgraphDeploymentID, fixedReserveRatio); - emit SubgraphUpgraded( - _l2SubgraphID, - subgraphData.vSignal, - transferData.tokens, - _subgraphDeploymentID - ); + emit SubgraphUpgraded(_l2SubgraphID, vSignal, transferData.tokens, _subgraphDeploymentID); emit SubgraphVersionUpdated(_l2SubgraphID, _subgraphDeploymentID, _versionMetadata); - emit SignalMinted(_l2SubgraphID, msg.sender, nSignal, vSignal, transferData.tokens); emit SubgraphL2TransferFinalized(_l2SubgraphID); } @@ -248,7 +254,13 @@ contract L2GNS is GNS, L2GNSV1Storage, IL2GNS { // The subgraph will be disabled until finishSubgraphTransferFromL1 is called subgraphData.disabled = true; - transferData.tokens = _tokens; + if (_tokens >= MINIMUM_CURATION_DEPOSIT) { + transferData.tokens = _tokens; + } else { + // transferData.tokens stays uninitialized at zero + graphToken().transfer(_subgraphOwner, _tokens); + emit CuratorBalanceReturnedToBeneficiary(_l1SubgraphID, _subgraphOwner, _tokens); + } transferData.subgraphReceivedOnL2BlockNumber = block.number; // Mint the NFT. Use the subgraphID as tokenID. @@ -279,7 +291,7 @@ contract L2GNS is GNS, L2GNSV1Storage, IL2GNS { SubgraphData storage subgraphData = _getSubgraphData(l2SubgraphID); // If subgraph transfer wasn't finished, we should send the tokens to the curator - if (!transferData.l2Done || subgraphData.disabled) { + if (!transferData.l2Done || subgraphData.disabled || _tokensIn < MINIMUM_CURATION_DEPOSIT) { graphToken().transfer(_curator, _tokensIn); emit CuratorBalanceReturnedToBeneficiary(_l1SubgraphID, _curator, _tokensIn); } else { diff --git a/contracts/staking/Staking.sol b/contracts/staking/Staking.sol index c61ed500f..07b98a301 100644 --- a/contracts/staking/Staking.sol +++ b/contracts/staking/Staking.sol @@ -358,18 +358,14 @@ abstract contract Staking is StakingV4Storage, GraphUpgradeable, IStakingBase, M AllocationState allocState = _getAllocationState(_allocationID); require(allocState != AllocationState.Null, "!collect"); - // Get allocation - Allocation storage alloc = __allocations[_allocationID]; - // The allocation must've been opened at least 1 epoch ago, - // to prevent manipulation of the curation or delegation pools - require(alloc.createdAtEpoch < epochManager().currentEpoch(), "!epoch"); - // If the query fees are zero, we don't want to revert // but we also don't need to do anything, so just return if (_tokens == 0) { return; } + // Get allocation + Allocation storage alloc = __allocations[_allocationID]; bytes32 subgraphDeploymentID = alloc.subgraphDeploymentID; uint256 queryFees = _tokens; // Tokens collected from the channel diff --git a/test/curation/configuration.test.ts b/test/curation/configuration.test.ts index f475d8f30..08eb936fc 100644 --- a/test/curation/configuration.test.ts +++ b/test/curation/configuration.test.ts @@ -81,6 +81,10 @@ describe('Curation:Config', () => { .setMinimumCurationDeposit(defaults.curation.minimumCurationDeposit) await expect(tx).revertedWith('Only Controller governor') }) + + it('should get `minimumCurationDeposit`', async function () { + expect(await curation.minimumCurationDeposit()).eq(defaults.curation.minimumCurationDeposit) + }) }) describe('curationTaxPercentage', function () { diff --git a/test/l2/l2Curation.test.ts b/test/l2/l2Curation.test.ts index b7732ebd0..fcaf19f5f 100644 --- a/test/l2/l2Curation.test.ts +++ b/test/l2/l2Curation.test.ts @@ -79,19 +79,9 @@ describe('L2Curation:Config', () => { }) describe('minimumCurationDeposit', function () { - it('should set `minimumCurationDeposit`', async function () { - // Set right in the constructor - expect(await curation.minimumCurationDeposit()).eq(defaults.curation.l2MinimumCurationDeposit) - - // Can set if allowed - const newValue = toBN('100') - await curation.connect(governor.signer).setMinimumCurationDeposit(newValue) - expect(await curation.minimumCurationDeposit()).eq(newValue) - }) - - it('reject set `minimumCurationDeposit` if out of bounds', async function () { - const tx = curation.connect(governor.signer).setMinimumCurationDeposit(0) - await expect(tx).revertedWith('Minimum curation deposit cannot be 0') + it('should revert trying to set `minimumCurationDeposit`', async function () { + const tx = curation.connect(governor.signer).setMinimumCurationDeposit(toGRT('1')) + await expect(tx).revertedWith('Not implemented in L2') }) it('reject set `minimumCurationDeposit` if not allowed', async function () { @@ -100,6 +90,10 @@ describe('L2Curation:Config', () => { .setMinimumCurationDeposit(defaults.curation.minimumCurationDeposit) await expect(tx).revertedWith('Only Controller governor') }) + + it('should get `minimumCurationDeposit`', async function () { + expect(await curation.minimumCurationDeposit()).eq(defaults.curation.l2MinimumCurationDeposit) + }) }) describe('curationTaxPercentage', function () { @@ -164,7 +158,7 @@ describe('L2Curation', () => { // Test values const signalAmountFor1000Tokens = toGRT('1000.0') - const signalAmountForMinimumCuration = toBN('1') + const signalAmountForMinimumCuration = toGRT('1') const subgraphDeploymentID = randomHexBytes() const curatorTokens = toGRT('1000000000') const tokensToDeposit = toGRT('1000') @@ -419,8 +413,6 @@ describe('L2Curation', () => { describe('curate', async function () { it('reject deposit below minimum tokens required', async function () { - // Set the minimum to a value greater than 1 so that we can test - await curation.connect(governor.signer).setMinimumCurationDeposit(toBN('2')) const tokensToDeposit = (await curation.minimumCurationDeposit()).sub(toBN(1)) const tx = curation.connect(curator.signer).mint(subgraphDeploymentID, tokensToDeposit, 0) await expect(tx).revertedWith('Curation deposit is below minimum required') @@ -469,8 +461,6 @@ describe('L2Curation', () => { }) it('reject deposit below minimum tokens required', async function () { - // Set the minimum to a value greater than 1 so that we can test - await curation.connect(governor.signer).setMinimumCurationDeposit(toBN('2')) const tokensToDeposit = (await curation.minimumCurationDeposit()).sub(toBN(1)) const tx = curation .connect(gnsImpersonator) @@ -613,6 +603,13 @@ describe('L2Curation', () => { await expect(tx).revertedWith('Cannot burn zero signal') }) + it('reject redeem if remaining tokens are less than minimum', async function () { + const tx = curation + .connect(curator.signer) + .burn(subgraphDeploymentID, tokensToDeposit.sub(1), 0) + await expect(tx).revertedWith('Less than minimum curation left') + }) + it('should allow to redeem *partially*', async function () { // Redeem just one signal const signalToRedeem = toGRT('1') @@ -627,29 +624,6 @@ describe('L2Curation', () => { await shouldBurn(signalToRedeem, expectedTokens) }) - it('should allow to redeem back below minimum deposit', async function () { - // Set the minimum to a value greater than 1 so that we can test - await curation.connect(governor.signer).setMinimumCurationDeposit(toGRT('1')) - - // Redeem "almost" all signal - const signal = await curation.getCuratorSignal(curator.address, subgraphDeploymentID) - const signalToRedeem = signal.sub(toGRT('0.000001')) - const expectedTokens = await curation.signalToTokens(subgraphDeploymentID, signalToRedeem) - await shouldBurn(signalToRedeem, expectedTokens) - - // The pool should have less tokens that required by minimumCurationDeposit - const afterPool = await curation.pools(subgraphDeploymentID) - expect(afterPool.tokens).lt(await curation.minimumCurationDeposit()) - - // Should be able to deposit more after being under minimumCurationDeposit - const tokensToDeposit = toGRT('1') - const { 0: expectedSignal } = await curation.tokensToSignal( - subgraphDeploymentID, - tokensToDeposit, - ) - await shouldMint(tokensToDeposit, expectedSignal) - }) - it('should revert redeem if over slippage', async function () { const signalToRedeem = await curation.getCuratorSignal(curator.address, subgraphDeploymentID) const expectedTokens = tokensToDeposit @@ -748,34 +722,5 @@ describe('L2Curation', () => { expect(toRound(toFloat(toBN(signal)))).eq(toRound(expectedSignal)) } }) - - it('should mint when using a different ratio between GRT and signal', async function () { - this.timeout(60000) // increase timeout for test runner - - // Setup edge case with 1 GRT = 1 wei signal - await curation.setMinimumCurationDeposit(toGRT('1')) - - const tokensToDepositMany = [ - toGRT('1000'), // should mint if we start with number above minimum deposit - toGRT('1000'), // every time it should mint proportionally the same GCS due to linear bonding curve... - toGRT('1000'), - toGRT('1000'), - toGRT('2000'), - toGRT('2000'), - toGRT('123'), - toGRT('1'), - ] - - // Mint multiple times - for (const tokensToDeposit of tokensToDepositMany) { - const tx = await curation - .connect(curator.signer) - .mint(subgraphDeploymentID, tokensToDeposit, 0) - const receipt = await tx.wait() - const event: Event = receipt.events.pop() - const signal = event.args['signal'] - expect(tokensToDeposit).eq(signal.mul(toGRT('1'))) // we compare 1 GRT : 1 wei ratio - } - }) }) }) diff --git a/test/lib/deployment.ts b/test/lib/deployment.ts index 4690b9b6d..7604bf223 100644 --- a/test/lib/deployment.ts +++ b/test/lib/deployment.ts @@ -38,7 +38,7 @@ export const defaults = { curation: { reserveRatio: toBN('500000'), minimumCurationDeposit: toGRT('100'), - l2MinimumCurationDeposit: toBN('1'), + l2MinimumCurationDeposit: toGRT('1'), curationTaxPercentage: 0, }, dispute: { diff --git a/test/staking/allocation.test.ts b/test/staking/allocation.test.ts index 93d96863f..948446c1e 100644 --- a/test/staking/allocation.test.ts +++ b/test/staking/allocation.test.ts @@ -633,11 +633,6 @@ describe('Staking:Allocation', () => { await expect(tx).revertedWith('!alloc') }) - it('reject collect if allocation has been open for less than 1 epoch', async function () { - const tx = staking.connect(indexer.signer).collect(tokensToCollect, allocationID) - await expect(tx).revertedWith('!epoch') - }) - it('reject collect if allocation does not exist', async function () { const invalidAllocationID = randomHexBytes(20) const tx = staking.connect(assetHolder.signer).collect(tokensToCollect, invalidAllocationID) From fa0dd99f6ba98185e1f95242731ecebcf9db70b2 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 2 Nov 2023 13:57:40 -0300 Subject: [PATCH 2/4] test: additional tests for below-minimum GNS transfers --- test/l2/l2GNS.test.ts | 74 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/test/l2/l2GNS.test.ts b/test/l2/l2GNS.test.ts index 728decacf..597e32cb9 100644 --- a/test/l2/l2GNS.test.ts +++ b/test/l2/l2GNS.test.ts @@ -300,6 +300,50 @@ describe('L2GNS', () => { expect(await gns.ownerOf(l2SubgraphId)).eq(me.address) }) + it('returns the curation to the owner if it is under the minimum', async function () { + const { l1SubgraphId } = await defaultL1SubgraphParams() + const curatedTokens = toGRT('0.1') + const callhookData = defaultAbiCoder.encode( + ['uint8', 'uint256', 'address'], + [toBN(0), l1SubgraphId, me.address], + ) + const ownerBalanceBefore = await grt.balanceOf(me.address) + const tx = gatewayFinalizeTransfer( + mockL1GNS.address, + gns.address, + curatedTokens, + callhookData, + ) + + const l2SubgraphId = await gns.getAliasedL2SubgraphID(l1SubgraphId) + + await expect(tx) + .emit(l2GraphTokenGateway, 'DepositFinalized') + .withArgs(mockL1GRT.address, mockL1GNS.address, gns.address, curatedTokens) + await expect(tx) + .emit(gns, 'SubgraphReceivedFromL1') + .withArgs(l1SubgraphId, l2SubgraphId, me.address, curatedTokens) + await expect(tx) + .emit(gns, 'CuratorBalanceReturnedToBeneficiary') + .withArgs(l1SubgraphId, me.address, curatedTokens) + + const transferData = await gns.subgraphL2TransferData(l2SubgraphId) + const subgraphData = await gns.subgraphs(l2SubgraphId) + + expect(transferData.tokens).eq(0) + expect(transferData.l2Done).eq(false) + expect(transferData.subgraphReceivedOnL2BlockNumber).eq(await latestBlock()) + + expect(subgraphData.vSignal).eq(0) + expect(subgraphData.nSignal).eq(0) + expect(subgraphData.subgraphDeploymentID).eq(HashZero) + expect(subgraphData.reserveRatioDeprecated).eq(DEFAULT_RESERVE_RATIO) + expect(subgraphData.disabled).eq(true) + expect(subgraphData.withdrawableGRT).eq(0) // Important so that it's not the same as a deprecated subgraph! + + expect(await gns.ownerOf(l2SubgraphId)).eq(me.address) + expect(await grt.balanceOf(me.address)).eq(ownerBalanceBefore.add(curatedTokens)) + }) it('does not conflict with a locally created subgraph', async function () { const l2Subgraph = await publishNewSubgraph(me, newSubgraph0, gns) @@ -683,6 +727,36 @@ describe('L2GNS', () => { // so the GNS balance should be the same expect(gnsBalanceAfter).eq(gnsBalanceBefore) }) + it('returns tokens to the beneficiary if they are less than the minimum', async function () { + const mockL1GNSL2Alias = await getL2SignerFromL1(mockL1GNS.address) + // Eth for gas: + await setAccountBalance(await mockL1GNSL2Alias.getAddress(), parseEther('1')) + + const { l1SubgraphId, curatedTokens, subgraphMetadata, versionMetadata } = + await defaultL1SubgraphParams() + await transferMockSubgraphFromL1( + l1SubgraphId, + curatedTokens, + subgraphMetadata, + versionMetadata, + ) + const curatorTokensBefore = await grt.balanceOf(me.address) + const gnsBalanceBefore = await grt.balanceOf(gns.address) + const callhookData = defaultAbiCoder.encode( + ['uint8', 'uint256', 'address'], + [toBN(1), l1SubgraphId, me.address], + ) + const tx = gatewayFinalizeTransfer(mockL1GNS.address, gns.address, toGRT('0.1'), callhookData) + await expect(tx) + .emit(gns, 'CuratorBalanceReturnedToBeneficiary') + .withArgs(l1SubgraphId, me.address, toGRT('0.1')) + const curatorTokensAfter = await grt.balanceOf(me.address) + expect(curatorTokensAfter).eq(curatorTokensBefore.add(toGRT('0.1'))) + const gnsBalanceAfter = await grt.balanceOf(gns.address) + // gatewayFinalizeTransfer will mint the tokens that are sent to the curator, + // so the GNS balance should be the same + expect(gnsBalanceAfter).eq(gnsBalanceBefore) + }) it('for an L2-native subgraph, it sends the tokens to the beneficiary', async function () { // This should never really happen unless there's a clash in subgraph IDs (which should // also never happen), but we test it anyway to ensure it's a well-defined behavior From 0c4f850050e59dcc3c39e4389f85f6a8c0650401 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Thu, 2 Nov 2023 20:51:32 -0300 Subject: [PATCH 3/4] fix(e2e): hardcoded L2 minimumCurationDeposit --- e2e/deployment/config/l2/l2Curation.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/e2e/deployment/config/l2/l2Curation.test.ts b/e2e/deployment/config/l2/l2Curation.test.ts index 809eb0ecd..fa5a3654f 100644 --- a/e2e/deployment/config/l2/l2Curation.test.ts +++ b/e2e/deployment/config/l2/l2Curation.test.ts @@ -2,6 +2,7 @@ import { expect } from 'chai' import hre from 'hardhat' import { getItemValue } from '../../../../cli/config' import GraphChain from '../../../../gre/helpers/chain' +import { toGRT } from '../../../../cli/network' describe('[L2] L2Curation configuration', () => { const graph = hre.graph() @@ -36,9 +37,9 @@ describe('[L2] L2Curation configuration', () => { expect(value).eq(expected) }) - it('minimumCurationDeposit should match "minimumCurationDeposit" in the config file', async function () { + it('minimumCurationDeposit should match the hardcoded value', async function () { const value = await L2Curation.minimumCurationDeposit() - const expected = getItemValue(graphConfig, 'contracts/L2Curation/init/minimumCurationDeposit') + const expected = toGRT('1') expect(value).eq(expected) }) }) From 37e35e563970172e1090ae444b612e95a8a2f167 Mon Sep 17 00:00:00 2001 From: Pablo Carranza Velez Date: Fri, 10 Nov 2023 11:24:02 -0300 Subject: [PATCH 4/4] fix: minimumCurationDeposit() can be pure in L2Curation --- contracts/l2/curation/L2Curation.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/l2/curation/L2Curation.sol b/contracts/l2/curation/L2Curation.sol index fbf19ba2c..697ed0bfb 100644 --- a/contracts/l2/curation/L2Curation.sol +++ b/contracts/l2/curation/L2Curation.sol @@ -451,7 +451,7 @@ contract L2Curation is CurationV2Storage, GraphUpgradeable, IL2Curation { * @notice Getter for minimum curation deposit * @return Minimum amount of tokens that must be deposited to mint signal */ - function minimumCurationDeposit() external view returns (uint256) { + function minimumCurationDeposit() external pure returns (uint256) { return MINIMUM_CURATION_DEPOSIT; }