diff --git a/contracts/curation/Curation.sol b/contracts/curation/Curation.sol index fc59b33d1..41460df3f 100644 --- a/contracts/curation/Curation.sol +++ b/contracts/curation/Curation.sol @@ -369,6 +369,20 @@ contract Curation is CurationV1Storage, GraphUpgradeable { return pools[_subgraphDeploymentID].tokens; } + /** + * @dev Get the reserve ratio for curation pool. + * @param _subgraphDeploymentID Subgraph deployment curation poool + * @return Reserve ratio for curation pool + */ + function getCurationPoolReserveRatio(bytes32 _subgraphDeploymentID) + external + view + override + returns (uint32) + { + return pools[_subgraphDeploymentID].reserveRatio; + } + /** * @dev Calculate amount of signal that can be bought with tokens in a curation pool. * This function considers and excludes the deposit tax. diff --git a/contracts/curation/ICuration.sol b/contracts/curation/ICuration.sol index 9e1701aaf..c2e9b7c8b 100644 --- a/contracts/curation/ICuration.sol +++ b/contracts/curation/ICuration.sol @@ -44,6 +44,11 @@ interface ICuration { function getCurationPoolTokens(bytes32 _subgraphDeploymentID) external view returns (uint256); + function getCurationPoolReserveRatio(bytes32 _subgraphDeploymentID) + external + view + returns (uint32); + function tokensToSignal(bytes32 _subgraphDeploymentID, uint256 _tokensIn) external view diff --git a/contracts/discovery/GNS.sol b/contracts/discovery/GNS.sol index 12a047210..c756d6df9 100644 --- a/contracts/discovery/GNS.sol +++ b/contracts/discovery/GNS.sol @@ -105,6 +105,15 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { bytes32 indexed subgraphDeploymentID ); + /** + * @dev Emitted when a subgraph upgrade is finalized + */ + event SubgraphUpgradeFinalized( + uint256 indexed subgraphID, + bytes32 indexed subgraphDeploymentID, + uint256 vSignal + ); + /** * @dev Emitted when a subgraph is deprecated. */ @@ -241,6 +250,9 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { subgraphData.subgraphDeploymentID = _subgraphDeploymentID; subgraphData.reserveRatio = defaultReserveRatio; + // Init version + subgraphData.versions[VersionType.Current].subgraphDeploymentID = _subgraphDeploymentID; + // Mint the NFT. Use the subgraphID as tokenId. // This function will check the if tokenId already exists. _mint(subgraphOwner, subgraphID); @@ -289,41 +301,149 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { // Move all signal from previous version to new version // NOTE: We will only do this as long as there is signal on the subgraph if (subgraphData.nSignal > 0) { - // Burn all version signal in the name pool for tokens (w/no slippage protection) - // Sell all signal from the old deployment - uint256 tokens = curation.burn( - subgraphData.subgraphDeploymentID, - subgraphData.vSignal, + uint256 tokens; + + // Init old version + _initOldVersion(subgraphData); + + tokens = curation.burn( + subgraphData.versions[VersionType.Current].subgraphDeploymentID, + subgraphData.versions[VersionType.Current].vSignal, 0 ); + if (_versionExists(subgraphData.versions[VersionType.New])) { + tokens = tokens.add( + curation.burn( + subgraphData.versions[VersionType.New].subgraphDeploymentID, + subgraphData.versions[VersionType.New].vSignal, + 0 + ) + ); + } + // Take the owner cut of the curation tax, add it to the total // Upgrade is only callable by the owner, we assume then that msg.sender = owner address subgraphOwner = msg.sender; + + // Tokens with tax uint256 tokensWithTax = _chargeOwnerTax( tokens, subgraphOwner, curation.curationTaxPercentage() ); - // Update pool: constant nSignal, vSignal can change (w/no slippage protection) - // Buy all signal from the new deployment - (subgraphData.vSignal, ) = curation.mint(_subgraphDeploymentID, tokensWithTax, 0); + // Divide tokens in half + uint256 splitTokens = tokensWithTax.div(2); - emit SubgraphUpgraded( - _subgraphID, - subgraphData.vSignal, - tokensWithTax, - _subgraphDeploymentID + // Mint half to old version + (uint256 _vSignalOld, ) = curation.mint( + subgraphData.subgraphDeploymentID, + splitTokens, + 0 + ); + + // Mint half to new version + (uint256 _vSignalNew, ) = curation.mint( + _subgraphDeploymentID, + (tokensWithTax.sub(splitTokens)), + 0 ); + + uint256 vSignalTotal = _vSignalOld.add(_vSignalNew); + + // Update vSignal for versions + subgraphData.versions[VersionType.Current].vSignal = _vSignalOld; + subgraphData.versions[VersionType.New].vSignal = _vSignalNew; + + // Update subgraphData Signal + subgraphData.vSignal = vSignalTotal; + + emit SubgraphUpgraded(_subgraphID, vSignalTotal, tokensWithTax, _subgraphDeploymentID); } - // Update target deployment + // Update versions + subgraphData.versions[VersionType.Current].subgraphDeploymentID = subgraphData + .subgraphDeploymentID; + subgraphData.versions[VersionType.New].subgraphDeploymentID = _subgraphDeploymentID; + + // Update deployment ID subgraphData.subgraphDeploymentID = _subgraphDeploymentID; emit SubgraphVersionUpdated(_subgraphID, _subgraphDeploymentID, _versionMetadata); } + /** + * @dev Finalize the upgrade process between two versions belonging to a subgraph + * @param _subgraphID Subgraph ID + */ + function finalizeSubgraphUpgrade(uint256 _subgraphID) + external + override + notPaused + onlySubgraphAuth(_subgraphID) + { + // Subgraph check + SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID); + + // Check new version exists + require( + subgraphData.versions[VersionType.New].subgraphDeploymentID != 0, + "GNS: New version does not exist" + ); + + ICuration curation = curation(); + + uint256 tokens; + + if (subgraphData.nSignal > 0) { + // Burn signal + tokens = curation.burn( + subgraphData.versions[VersionType.Current].subgraphDeploymentID, + subgraphData.versions[VersionType.Current].vSignal, + 0 + ); + tokens = tokens.add( + curation.burn( + subgraphData.versions[VersionType.New].subgraphDeploymentID, + subgraphData.versions[VersionType.New].vSignal, + 0 + ) + ); + + // Mint tokens + (uint256 _vSignalTotal, ) = curation.mint( + subgraphData.versions[VersionType.New].subgraphDeploymentID, + tokens, + 0 + ); + + // Update version signal + subgraphData.versions[VersionType.Current].vSignal = _vSignalTotal; + + // Update subgraph signal + subgraphData.vSignal = _vSignalTotal; + + // Remove New version signal + subgraphData.versions[VersionType.New].vSignal = 0; + } + + // Update Current version subgraphDeploymentID + subgraphData.versions[VersionType.Current].subgraphDeploymentID = subgraphData + .versions[VersionType.New] + .subgraphDeploymentID; + + // Update New version subgraphDeploymentID + subgraphData.versions[VersionType.New].subgraphDeploymentID = 0; + + // TODO: These events might need to be updated + emit SubgraphUpgradeFinalized( + _subgraphID, + subgraphData.subgraphDeploymentID, + subgraphData.versions[VersionType.Current].vSignal + ); + } + /** * @dev Deprecate a subgraph. The bonding curve is destroyed, the vSignal is burned, and the GNS * contract holds the GRT from burning the vSignal, which all curators can withdraw manually. @@ -339,22 +459,50 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { // Subgraph check SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID); - // Burn signal only if it has any available + // Init old version + _initOldVersion(subgraphData); + + uint256 _withdrawableGRT; + if (subgraphData.nSignal > 0) { - subgraphData.withdrawableGRT = curation().burn( - subgraphData.subgraphDeploymentID, - subgraphData.vSignal, + _withdrawableGRT = curation().burn( + subgraphData.versions[VersionType.Current].subgraphDeploymentID, + subgraphData.versions[VersionType.Current].vSignal, 0 ); + + subgraphData.versions[VersionType.Current].subgraphDeploymentID = 0; + subgraphData.versions[VersionType.Current].vSignal = 0; + + // If new version exists + if (_versionExists(subgraphData.versions[VersionType.New])) { + _withdrawableGRT = _withdrawableGRT.add( + curation().burn( + subgraphData.versions[VersionType.New].subgraphDeploymentID, + subgraphData.versions[VersionType.New].vSignal, + 0 + ) + ); + + subgraphData.versions[VersionType.New].subgraphDeploymentID = 0; + subgraphData.versions[VersionType.New].vSignal = 0; + } + + subgraphData.withdrawableGRT = _withdrawableGRT; } // Deprecate the subgraph and do cleanup subgraphData.disabled = true; subgraphData.vSignal = 0; subgraphData.reserveRatio = 0; - // NOTE: We don't reset the following variable as we use it to test if the Subgraph was ever created + + // NOTE: We don't reset subgraphDeploymentID + // in order to test if the Subgraph was ever created // subgraphData.subgraphDeploymentID = 0; + // NOTE: We don't reset nSignal to allow withdrawals after deprecation + // subgraphData.nSignal = 0; + // Burn the NFT _burn(_subgraphID); @@ -375,23 +523,61 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { // Subgraph checks SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID); - // Pull tokens from sender + // Init old version + _initOldVersion(subgraphData); + address curator = msg.sender; + uint256 _tokens = _tokensIn; + uint256 vSignalTotal; + + // Pull tokens from sender TokenUtils.pullTokens(graphToken(), curator, _tokensIn); + // If new version exists + if (_versionExists(subgraphData.versions[VersionType.New])) { + _tokens = _tokens.div(2); + + (vSignalTotal, ) = curation().mint( + subgraphData.versions[VersionType.New].subgraphDeploymentID, + _tokens, + 0 + ); + subgraphData.versions[VersionType.New].vSignal = subgraphData + .versions[VersionType.New] + .vSignal + .add(vSignalTotal); + + // Subtract from total to get an accurate remainder + _tokens = _tokensIn.sub(_tokens); + } + // Get name signal to mint for tokens deposited - (uint256 vSignal, ) = curation().mint(subgraphData.subgraphDeploymentID, _tokensIn, 0); - uint256 nSignal = vSignalToNSignal(_subgraphID, vSignal); + (uint256 vSignalOld, ) = curation().mint( + subgraphData.versions[VersionType.Current].subgraphDeploymentID, + _tokens, + 0 + ); + vSignalTotal = vSignalTotal.add(vSignalOld); + + uint256 nSignalTotal = vSignalToNSignal(_subgraphID, vSignalTotal); // Slippage protection - require(nSignal >= _nSignalOutMin, "GNS: Slippage protection"); + require(nSignalTotal >= _nSignalOutMin, "GNS: Slippage protection"); + + // Update version vSignal + subgraphData.versions[VersionType.Current].vSignal = subgraphData + .versions[VersionType.Current] + .vSignal + .add(vSignalOld); // Update pools - subgraphData.vSignal = subgraphData.vSignal.add(vSignal); - subgraphData.nSignal = subgraphData.nSignal.add(nSignal); - subgraphData.curatorNSignal[curator] = subgraphData.curatorNSignal[curator].add(nSignal); + subgraphData.vSignal = subgraphData.vSignal.add(vSignalTotal); + subgraphData.nSignal = subgraphData.nSignal.add(nSignalTotal); + subgraphData.curatorNSignal[curator] = subgraphData.curatorNSignal[curator].add( + nSignalTotal + ); - emit SignalMinted(_subgraphID, curator, nSignal, vSignal, _tokensIn); + emit SignalMinted(_subgraphID, curator, nSignalTotal, vSignalTotal, _tokensIn); } /** @@ -408,6 +594,9 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { // Subgraph checks SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID); + // Init old version + _initOldVersion(subgraphData); + // Curator balance checks address curator = msg.sender; uint256 curatorNSignal = subgraphData.curatorNSignal[curator]; @@ -416,19 +605,54 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { "GNS: Curator cannot withdraw more nSignal than they have" ); - // Get tokens for name signal amount to burn - uint256 vSignal = nSignalToVSignal(_subgraphID, _nSignal); - uint256 tokens = curation().burn(subgraphData.subgraphDeploymentID, vSignal, _tokensOutMin); + bool newVersionExists = _versionExists(subgraphData.versions[VersionType.New]); + uint256 vSignalTotal = nSignalToVSignal(_subgraphID, _nSignal); + uint256 vSignal = vSignalTotal; + uint256 tokens; + + // If new version exists + if (newVersionExists) { + vSignal = vSignalTotal.div(2); + + (tokens, vSignal) = _tryBurn( + subgraphData.versions[VersionType.New], + vSignal, + _tokensOutMin + ); + + subgraphData.versions[VersionType.New].vSignal = subgraphData + .versions[VersionType.New] + .vSignal + .sub(vSignal); + + // Subtract from total to get an accurate remainder + vSignal = vSignalTotal.sub(vSignal); + } + + (uint256 currentTokens, ) = _tryBurn( + subgraphData.versions[VersionType.Current], + vSignal, + _tokensOutMin + ); + + tokens = tokens.add(currentTokens); + + // Update version vSignal + subgraphData.versions[VersionType.Current].vSignal = subgraphData + .versions[VersionType.Current] + .vSignal + .sub(vSignal); + // Update pools - subgraphData.vSignal = subgraphData.vSignal.sub(vSignal); + subgraphData.vSignal = subgraphData.vSignal.sub(vSignalTotal); subgraphData.nSignal = subgraphData.nSignal.sub(_nSignal); subgraphData.curatorNSignal[curator] = subgraphData.curatorNSignal[curator].sub(_nSignal); // Return the tokens to the nameCurator require(graphToken().transfer(curator, tokens), "GNS: Error sending tokens"); - emit SignalBurned(_subgraphID, curator, _nSignal, vSignal, tokens); + emit SignalBurned(_subgraphID, curator, _nSignal, vSignalTotal, tokens); } /** @@ -521,12 +745,37 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { ) { SubgraphData storage subgraphData = _getSubgraphData(_subgraphID); - (uint256 vSignal, uint256 curationTax) = curation().tokensToSignal( - subgraphData.subgraphDeploymentID, - _tokensIn + + uint256 vSignalTotal; + uint256 tokens = _tokensIn; + + // If new version exists + if (_versionExists(subgraphData.versions[VersionType.New])) { + // Divide signal in half + tokens = _tokensIn.div(2); + + (uint256 vSignalNew, ) = curation().tokensToSignal( + subgraphData.versions[VersionType.New].subgraphDeploymentID, + tokens + ); + + vSignalTotal = vSignalTotal.add(vSignalNew); + + // Subtract from total to get an accurate remainder + tokens = _tokensIn.sub(tokens); + } + + (uint256 vSignalOld, uint256 curationTax) = curation().tokensToSignal( + subgraphData.versions[VersionType.Current].subgraphDeploymentID, + tokens ); - uint256 nSignal = vSignalToNSignal(_subgraphID, vSignal); - return (vSignal, nSignal, curationTax); + + vSignalTotal = vSignalTotal.add(vSignalOld); + + uint256 nSignal = vSignalToNSignal(_subgraphID, vSignalTotal); + + // TODO: maybe pass both curationTaxes + return (vSignalTotal, nSignal, curationTax); } /** @@ -544,8 +793,37 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { // Get subgraph or revert if not published // It does not make sense to convert signal from a disabled or non-existing one SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID); - uint256 vSignal = nSignalToVSignal(_subgraphID, _nSignalIn); - uint256 tokensOut = curation().signalToTokens(subgraphData.subgraphDeploymentID, vSignal); + + uint256 vSignalTotal = nSignalToVSignal(_subgraphID, _nSignalIn); + uint256 vSignal = vSignalTotal; + uint256 tokensOut; + + if (_versionExists(subgraphData.versions[VersionType.New])) { + // Divide signal in half + vSignal = vSignalTotal.div(2); + + tokensOut = tokensOut.add( + curation().signalToTokens( + subgraphData.versions[VersionType.New].subgraphDeploymentID, + vSignal + ) + ); + + // Subtract from total to get an accurate remainder + vSignal = vSignalTotal.sub(vSignal); + } + + bytes32 subgraphDeploymentID; + + // Check if Current version exists + if (_versionExists(subgraphData.versions[VersionType.Current])) { + subgraphDeploymentID = subgraphData.versions[VersionType.Current].subgraphDeploymentID; + } else { + subgraphDeploymentID = subgraphData.subgraphDeploymentID; + } + + tokensOut = tokensOut.add(curation().signalToTokens(subgraphDeploymentID, vSignal)); + return (vSignal, tokensOut); } @@ -614,6 +892,23 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { return _getSubgraphData(_subgraphID).curatorNSignal[_curator]; } + /** + * @dev Get the version subgraphDeploymentID and vSignal. + * @param _subgraphID Subgraph ID + * @param _version Version uint256 + * @return Version struct properties + */ + function getSubgraphVersion(uint256 _subgraphID, VersionType _version) + public + view + override + returns (bytes32, uint256) + { + Version memory v = _getSubgraphData(_subgraphID).versions[_version]; + + return (v.subgraphDeploymentID, v.vSignal); + } + /** * @dev Return the total signal on the subgraph. * @param _subgraphID Subgraph ID @@ -757,4 +1052,48 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall { require(_isPublished(subgraphData) == true, "GNS: Must be active"); return subgraphData; } + + /** + * @dev Check to see if verrsion exists + * @param _version Version data + * @return Bool Whether the version exists or not + */ + function _versionExists(Version storage _version) internal view returns (bool) { + return _version.subgraphDeploymentID != 0; + } + + /** + * @dev Initialize current version + * @param _subgraphData Subgraph data + */ + function _initOldVersion(SubgraphData storage _subgraphData) internal { + if (!_versionExists(_subgraphData.versions[VersionType.Current])) { + _subgraphData.versions[VersionType.Current].subgraphDeploymentID = _subgraphData + .subgraphDeploymentID; + _subgraphData.versions[VersionType.Current].vSignal = _subgraphData.vSignal; + } + } + + /** + * @dev Try curation.burn, if it fails burn all vSignal + * @param _version Version data + * @param _vSignal Version signal + * @param _tokensOutMin Expected minimum amount of tokens to receive + */ + function _tryBurn( + Version storage _version, + uint256 _vSignal, + uint256 _tokensOutMin + ) internal returns (uint256, uint256) { + try curation().burn(_version.subgraphDeploymentID, _vSignal, _tokensOutMin) returns ( + uint256 tokens + ) { + return (tokens, _vSignal); + } catch { + return ( + curation().burn(_version.subgraphDeploymentID, _version.vSignal, _tokensOutMin), + _version.vSignal + ); + } + } } diff --git a/contracts/discovery/IGNS.sol b/contracts/discovery/IGNS.sol index 02926bb11..875ee6c03 100644 --- a/contracts/discovery/IGNS.sol +++ b/contracts/discovery/IGNS.sol @@ -6,13 +6,14 @@ interface IGNS { // -- Pool -- struct SubgraphData { - uint256 vSignal; // The token of the subgraph-deployment bonding curve + uint256 vSignal; // The sum of all deployment vSignal uint256 nSignal; // The token of the subgraph bonding curve mapping(address => uint256) curatorNSignal; bytes32 subgraphDeploymentID; uint32 reserveRatio; bool disabled; uint256 withdrawableGRT; + mapping(VersionType => Version) versions; } struct LegacySubgraphKey { @@ -20,6 +21,16 @@ interface IGNS { uint256 accountSeqID; } + struct Version { + bytes32 subgraphDeploymentID; + uint256 vSignal; + } + + enum VersionType { + Current, + New + } + // -- Configuration -- function approveAll() external; @@ -51,6 +62,8 @@ interface IGNS { bytes32 _versionMetadata ) external; + function finalizeSubgraphUpgrade(uint256 _subgraphID) external; + function deprecateSubgraph(uint256 _subgraphID) external; // -- Curation -- @@ -104,5 +117,10 @@ interface IGNS { view returns (uint256); + function getSubgraphVersion(uint256 _subgraphID, VersionType _version) + external + view + returns (bytes32, uint256); + function isPublished(uint256 _subgraphID) external view returns (bool); } diff --git a/graph.config.yml b/graph.config.yml index 65741fc68..ccfb2d768 100644 --- a/graph.config.yml +++ b/graph.config.yml @@ -39,7 +39,7 @@ contracts: initialSupply: "10000000000000000000000000000" # 10,000,000,000 GRT calls: - fn: "addMinter" - minter: "${{RewardsManager.address}}" + minter: "${{RewardsManager.address}}" Curation: proxy: true init: diff --git a/test/gns.test.ts b/test/gns.test.ts index ad31afc4d..62e2e1a8b 100644 --- a/test/gns.test.ts +++ b/test/gns.test.ts @@ -9,6 +9,7 @@ import { Curation } from '../build/types/Curation' import { getAccounts, randomHexBytes, Account, toGRT } from './lib/testHelpers' import { NetworkFixture } from './lib/fixtures' import { toBN, formatGRT } from './lib/testHelpers' +import { equal } from 'assert/strict' const { AddressZero } = ethers.constants @@ -34,6 +35,18 @@ interface AccountDefaultName { nameIdentifier: string } +interface ExpectedMintSignalValues { + splitTokens?: BigInt + tokensOld?: BigInt + vSignalOld?: BigInt + tokensNew?: BigInt + vSignalNew?: BigInt + nSignal?: BigInt + vSignal?: BigInt + vSignalEmit?: BigInt + tokensEmit?: BigInt +} + // Utils const DEFAULT_RESERVE_RATIO = 1000000 @@ -46,6 +59,8 @@ describe('GNS', () => { let me: Account let other: Account let governor: Account + let curator: Account + let otherCurator: Account let fixture: NetworkFixture @@ -60,6 +75,7 @@ describe('GNS', () => { let newSubgraph0: PublishSubgraph let newSubgraph1: PublishSubgraph + let newSubgraph2: PublishSubgraph let defaultName: AccountDefaultName const buildSubgraph = (): PublishSubgraph => { @@ -77,7 +93,9 @@ describe('GNS', () => { } } - const getTokensAndVSignal = async (subgraphDeploymentID: string): Promise> => { + const curationPoolTokensAndSignal = async ( + subgraphDeploymentID: string, + ): Promise> => { const curationPool = await curation.pools(subgraphDeploymentID) const vSignal = await curation.getCurationPoolSignal(subgraphDeploymentID) return [curationPool.tokens, vSignal] @@ -182,96 +200,122 @@ describe('GNS', () => { account: Account, subgraphID: string, newSubgraph: PublishSubgraph, + splitTokens: BigInt = 4631250000000000000000n, + splitVSignal: BigInt = 6805328794408099720n, + totalVSignal: BigInt = 13610657588816199440n, + totalNSignal: BigInt = 9746794344808963906n, + totalTokensIncludingTax: BigInt = 9750000000000000000000n, ) => { - // Before state - const ownerTaxPercentage = await gns.ownerTaxPercentage() - const curationTaxPercentage = await curation.curationTaxPercentage() + // Subgraph before transaction const beforeSubgraph = await gns.subgraphs(subgraphID) - // Check what selling all nSignal, which == selling all vSignal, should return for tokens - // NOTE - no tax on burning on nSignal - const tokensReceivedEstimate = beforeSubgraph.nSignal.gt(0) - ? (await gns.nSignalToTokens(subgraphID, beforeSubgraph.nSignal))[1] - : toBN(0) - // Example: - // Deposit 100, 5 is taxed, 95 GRT in curve - // Upgrade - calculate 5% tax on 95 --> 4.75 GRT - // Multiple by ownerPercentage --> 50% * 4.75 = 2.375 GRT - // Owner adds 2.375 to 90.25, we deposit 92.625 GRT into the curve - // Divide this by 0.95 to get exactly 97.5 total tokens to be deposited - - // nSignalToTokens returns the amount of tokens with tax removed - // already. So we must add in the tokens removed - const MAX_PPM = 1000000 - const taxOnOriginal = tokensReceivedEstimate.mul(curationTaxPercentage).div(MAX_PPM) - const totalWithoutOwnerTax = tokensReceivedEstimate.sub(taxOnOriginal) - const ownerTax = taxOnOriginal.mul(ownerTaxPercentage).div(MAX_PPM) - const totalWithOwnerTax = totalWithoutOwnerTax.add(ownerTax) - const totalAdjustedUp = totalWithOwnerTax.mul(MAX_PPM).div(MAX_PPM - curationTaxPercentage) - - // Re-estimate amount of signal to get considering the owner tax paid by the owner - - const { 0: newVSignalEstimate, 1: newCurationTaxEstimate } = beforeSubgraph.nSignal.gt(0) - ? await curation.tokensToSignal(newSubgraph.subgraphDeploymentID, totalAdjustedUp) - : [toBN(0), toBN(0)] - // Send tx - const tx = gns + const tx = await gns .connect(account.signer) .publishNewVersion(subgraphID, newSubgraph.subgraphDeploymentID, newSubgraph.versionMetadata) + + // Subgraph after transaction + const afterSubgraph = await gns.subgraphs(subgraphID) + + // Versions + const [idOld, vSignalOld] = await gns.getSubgraphVersion(subgraphID, 0) + const [idNew, vSignalNew] = await gns.getSubgraphVersion(subgraphID, 1) + + // Owner of subgraph + const owner = await gns.ownerOf(subgraphID) + + // Get curation pool data from previous deployment + const [tokensBefore, vSignalBefore] = await curationPoolTokensAndSignal( + beforeSubgraph.subgraphDeploymentID, + ) + + // Get curation pool data from current deployment + const [tokensAfter, vSignalAfter] = await curationPoolTokensAndSignal( + newSubgraph.subgraphDeploymentID, + ) + const txResult = expect(tx) .emit(gns, 'SubgraphVersionUpdated') .withArgs(subgraphID, newSubgraph.subgraphDeploymentID, newSubgraph.versionMetadata) - // Only emits this event if there was actual signal to upgrade + //Only emits this event if there was actual signal to upgrade if (beforeSubgraph.nSignal.gt(0)) { txResult .emit(gns, 'SubgraphUpgraded') - .withArgs(subgraphID, newVSignalEstimate, totalAdjustedUp, newSubgraph.subgraphDeploymentID) + .withArgs( + subgraphID, + totalVSignal, + totalTokensIncludingTax, + newSubgraph.subgraphDeploymentID, + ) } await txResult - // Check curation vSignal old are set to zero - const [afterTokensOldCuration, afterVSignalOldCuration] = await getTokensAndVSignal( - beforeSubgraph.subgraphDeploymentID, - ) - expect(afterTokensOldCuration).eq(0) - expect(afterVSignalOldCuration).eq(0) + // Check tokens and vSignal for previous deployment + expect(tokensBefore).eq(BigNumber.from(splitTokens)) + expect(vSignalBefore).eq(BigNumber.from(splitVSignal)) - // Check the vSignal of the new curation curve, and tokens - const [afterTokensNewCurve, afterVSignalNewCurve] = await getTokensAndVSignal( - newSubgraph.subgraphDeploymentID, - ) - expect(afterTokensNewCurve).eq(totalAdjustedUp.sub(newCurationTaxEstimate)) - expect(afterVSignalNewCurve).eq(newVSignalEstimate) + // Check tokens and vSignal for current deployment + expect(tokensAfter).eq(BigNumber.from(splitTokens)) + expect(vSignalAfter).eq(BigNumber.from(splitVSignal)) - // Check the nSignal pool - const afterSubgraph = await gns.subgraphs(subgraphID) - expect(afterSubgraph.vSignal).eq(afterVSignalNewCurve).eq(newVSignalEstimate) - expect(afterSubgraph.nSignal).eq(beforeSubgraph.nSignal) // should not change + // Check subgraph + expect(afterSubgraph.vSignal).eq(BigNumber.from(totalVSignal)) + expect(afterSubgraph.nSignal).eq(BigNumber.from(totalNSignal)) expect(afterSubgraph.subgraphDeploymentID).eq(newSubgraph.subgraphDeploymentID) + // Check versions + expect(vSignalOld).eq(BigNumber.from(splitVSignal)) + expect(vSignalNew).eq(BigNumber.from(splitVSignal)) + + // Check subgraph deployment IDs + expect(idOld).eq(beforeSubgraph.subgraphDeploymentID) + expect(idNew).eq(afterSubgraph.subgraphDeploymentID) + // Check NFT should not change owner - const owner = await gns.ownerOf(subgraphID) expect(owner).eq(account.address) return tx } const deprecateSubgraph = async (account: Account, subgraphID: string) => { + const [idOld] = await gns.getSubgraphVersion(subgraphID, 0) + const [idNew] = await gns.getSubgraphVersion(subgraphID, 1) + // Before state + let tokens const beforeSubgraph = await gns.subgraphs(subgraphID) - const [beforeTokens] = await getTokensAndVSignal(beforeSubgraph.subgraphDeploymentID) + const [oldTokens] = await curationPoolTokensAndSignal(idOld) + const [newTokens] = await curationPoolTokensAndSignal(idNew) + tokens = oldTokens.add(newTokens) // We can use the whole amount, since in this test suite all vSignal is used to be staked on nSignal const ownerBalanceBefore = await grt.balanceOf(account.address) // Send tx const tx = gns.connect(account.signer).deprecateSubgraph(subgraphID) - await expect(tx).emit(gns, 'SubgraphDeprecated').withArgs(subgraphID, beforeTokens) - // After state + // Check args + await expect(tx).emit(gns, 'SubgraphDeprecated').withArgs(subgraphID, tokens) + + // Subgraph after transaction const afterSubgraph = await gns.subgraphs(subgraphID) + + // Versions + const [, vSignalOld] = await gns.getSubgraphVersion(subgraphID, 0) + const [, vSignalNew] = await gns.getSubgraphVersion(subgraphID, 1) + + const [afterTokensOld, afterVSignalOld] = await curationPoolTokensAndSignal(idOld) + const [afterTokensNew, afterVSignalNew] = await curationPoolTokensAndSignal(idNew) + + // Check versions + expect(vSignalOld).eq(BigNumber.from(0)) + expect(vSignalNew).eq(BigNumber.from(0)) + expect(afterTokensOld).eq(BigNumber.from(0)) + expect(afterVSignalOld).eq(BigNumber.from(0)) + expect(afterTokensNew).eq(BigNumber.from(0)) + expect(afterVSignalNew).eq(BigNumber.from(0)) + // Check marked as deprecated expect(afterSubgraph.disabled).eq(true) // Signal for the deployment must be all burned @@ -279,7 +323,7 @@ describe('GNS', () => { // Cleanup reserve ratio expect(afterSubgraph.reserveRatio).eq(0) // Should be equal since owner pays curation tax - expect(afterSubgraph.withdrawableGRT).eq(beforeTokens) + expect(afterSubgraph.withdrawableGRT).eq(tokens) // Check balance of GNS increased by curation tax from owner being added const afterGNSBalance = await grt.balanceOf(gns.address) @@ -302,7 +346,7 @@ describe('GNS', () => { newSubgraphDeplyomentID: string, ): Promise => { // Before stats for the old vSignal curve - const beforeTokensVSigOldCuration = await getTokensAndVSignal(subgraph0.subgraphDeploymentID) + const beforeTokensVSigOldCuration = await curationPoolTokensAndSignal(subgraph0.subgraphDeploymentID) const beforeTokensOldCuration = beforeTokensVSigOldCuration[0] const beforeVSignalOldCuration = beforeTokensVSigOldCuration[1] @@ -344,14 +388,14 @@ describe('GNS', () => { ) // Check curation vSignal old was lowered and tokens too - const [afterTokensOldCuration, vSigAfterOldCuration] = await getTokensAndVSignal( + const [afterTokensOldCuration, vSigAfterOldCuration] = await curationPoolTokensAndSignal( subgraph0.subgraphDeploymentID, ) expect(afterTokensOldCuration).eq(beforeTokensOldCuration.sub(upgradeTokenReturn)) expect(vSigAfterOldCuration).eq(beforeVSignalOldCuration.sub(vSignalBurnEstimate)) // Check the vSignal of the new curation curve, amd tokens - const [afterTokensNewCurve, vSigAfterNewCurve] = await getTokensAndVSignal( + const [afterTokensNewCurve, vSigAfterNewCurve] = await curationPoolTokensAndSignal( newSubgraphDeplyomentID, ) expect(afterTokensNewCurve).eq(upgradeTokenReturn) @@ -374,69 +418,111 @@ describe('GNS', () => { account: Account, subgraphID: string, tokensIn: BigNumber, + expected: ExpectedMintSignalValues = {}, ): Promise => { - // Before state - const beforeSubgraph = await gns.subgraphs(subgraphID) - const [beforeTokens, beforeVSignal] = await getTokensAndVSignal( - beforeSubgraph.subgraphDeploymentID, - ) - - // Deposit - const { - 0: vSignalExpected, - 1: nSignalExpected, - 2: curationTax, - } = await gns.tokensToNSignal(subgraphID, tokensIn) + const tokensExpectedOld = expected.tokensOld || 9500000000000000000000n + const vSignalExpectedOld = expected.vSignalOld || 9746794344808963906n + const vSignalExpectedNew = expected.vSignalNew || 0 + const tokensExpectedNew = expected.tokensNew || 0 + const nSignalExpected = expected.nSignal || 9746794344808963906n + const vSignalExpected = expected.vSignal || 9746794344808963906n + + // Mint const tx = gns.connect(account.signer).mintSignal(subgraphID, tokensIn, 0) + + // Send tx await expect(tx) .emit(gns, 'SignalMinted') - .withArgs(subgraphID, account.address, nSignalExpected, vSignalExpected, tokensIn) + .withArgs( + subgraphID, + account.address, + BigNumber.from(nSignalExpected), + BigNumber.from(vSignalExpected), + tokensIn, + ) + + // Versions after transaction + const [idOld, vSignalOld] = await gns.getSubgraphVersion(subgraphID, 0) + const [idNew, vSignalNew] = await gns.getSubgraphVersion(subgraphID, 1) - // After state + // SubgraphData after transaction const afterSubgraph = await gns.subgraphs(subgraphID) - const [afterTokens, afterVSignal] = await getTokensAndVSignal( - afterSubgraph.subgraphDeploymentID, - ) + const [afterTokensOld, afterVSignalOld] = await curationPoolTokensAndSignal(idOld) + const [afterTokensNew, afterVSignalNew] = await curationPoolTokensAndSignal(idNew) - // Check state - expect(afterTokens).eq(beforeTokens.add(tokensIn.sub(curationTax))) - expect(afterVSignal).eq(beforeVSignal.add(vSignalExpected)) - expect(afterSubgraph.nSignal).eq(beforeSubgraph.nSignal.add(nSignalExpected)) - expect(afterSubgraph.vSignal).eq(beforeVSignal.add(vSignalExpected)) + // Check curation pool tokens and signal + expect(afterTokensOld).eq(BigNumber.from(tokensExpectedOld)) + expect(afterVSignalOld).eq(BigNumber.from(vSignalExpectedOld)) + + expect(afterTokensNew).eq(BigNumber.from(tokensExpectedNew)) + expect(afterVSignalNew).eq(BigNumber.from(vSignalExpectedNew)) + + // Check GNS signal + expect(afterSubgraph.nSignal).eq(BigNumber.from(nSignalExpected)) + expect(afterSubgraph.vSignal).eq(BigNumber.from(vSignalExpected)) + + // Check versions + expect(vSignalOld).eq(BigNumber.from(vSignalExpectedOld)) + expect(vSignalNew).eq(BigNumber.from(vSignalExpectedNew)) return tx } - const burnSignal = async (account: Account, subgraphID: string): Promise => { - // Before state - const beforeSubgraph = await gns.subgraphs(subgraphID) - const [beforeTokens, beforeVSignal] = await getTokensAndVSignal( - beforeSubgraph.subgraphDeploymentID, - ) - const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, account.address) + const burnSignal = async ( + account: Account, + subgraphID: string, + expected: ExpectedMintSignalValues = {}, + ): Promise => { + const tokensExpectedOld = expected.tokensOld || 0 + const vSignalExpectedOld = expected.vSignalOld || 0 + const vSignalExpectedNew = expected.vSignalNew || 0 + const tokensExpectedNew = expected.tokensNew || 0 + const nSignalExpected = expected.nSignal || 0 + const vSignalExpected = expected.vSignal || 0 + const vSignalEmit = expected.vSignalEmit || 9746794344808963906n + const tokensEmit = expected.tokensEmit || 9500000000000000000000n - // Withdraw - const { 0: vSignalExpected, 1: tokensExpected } = await gns.nSignalToTokens( - subgraphID, - beforeUsersNSignal, - ) + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, account.address) - // Send tx + // Burn const tx = gns.connect(account.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + // Check args await expect(tx) .emit(gns, 'SignalBurned') - .withArgs(subgraphID, account.address, beforeUsersNSignal, vSignalExpected, tokensExpected) + .withArgs( + subgraphID, + account.address, + beforeUsersNSignal, + BigNumber.from(vSignalEmit), + BigNumber.from(tokensEmit), + ) + + // Versions after transaction + const [idOld, vSignalOld] = await gns.getSubgraphVersion(subgraphID, 0) + const [idNew, vSignalNew] = await gns.getSubgraphVersion(subgraphID, 1) - // After state + // SubgraphData after transaction const afterSubgraph = await gns.subgraphs(subgraphID) - const [afterTokens, afterVSignalCuration] = await getTokensAndVSignal( - afterSubgraph.subgraphDeploymentID, - ) - // Check state - expect(afterTokens).eq(beforeTokens.sub(tokensExpected)) - expect(afterVSignalCuration).eq(beforeVSignal.sub(vSignalExpected)) - expect(afterSubgraph.nSignal).eq(beforeSubgraph.nSignal.sub(beforeUsersNSignal)) + // Curation Pool data after transaction + const [afterTokensOld, afterVSignalOld] = await curationPoolTokensAndSignal(idOld) + const [afterTokensNew, afterVSignalNew] = await curationPoolTokensAndSignal(idNew) + + // Check curation pool tokens and signal + expect(afterTokensOld).eq(BigNumber.from(tokensExpectedOld)) + expect(afterVSignalOld).eq(BigNumber.from(vSignalExpectedOld)) + + expect(afterTokensNew).eq(BigNumber.from(tokensExpectedNew)) + expect(afterVSignalNew).eq(BigNumber.from(vSignalExpectedNew)) + + // Check GNS signal + expect(afterSubgraph.nSignal).eq(BigNumber.from(nSignalExpected)) + expect(afterSubgraph.vSignal).eq(BigNumber.from(vSignalExpected)) + + // Check versions + expect(vSignalOld).eq(BigNumber.from(vSignalExpectedOld)) + expect(vSignalNew).eq(BigNumber.from(vSignalExpectedNew)) return tx } @@ -472,19 +558,26 @@ describe('GNS', () => { } before(async function () { - ;[me, other, governor] = await getAccounts() + ;[me, other, governor, curator, otherCurator] = await getAccounts() fixture = new NetworkFixture() ;({ grt, curation, gns } = await fixture.load(governor.signer)) newSubgraph0 = buildSubgraph() newSubgraph1 = buildSubgraph() + newSubgraph2 = buildSubgraph() defaultName = createDefaultName('graph') // Give some funds to the signers and approve gns contract to use funds on signers behalf await grt.connect(governor.signer).mint(me.address, tokens100000) await grt.connect(governor.signer).mint(other.address, tokens100000) + await grt.connect(governor.signer).mint(curator.address, tokens100000) + await grt.connect(governor.signer).mint(otherCurator.address, tokens100000) await grt.connect(me.signer).approve(gns.address, tokens100000) await grt.connect(me.signer).approve(curation.address, tokens100000) await grt.connect(other.signer).approve(gns.address, tokens100000) await grt.connect(other.signer).approve(curation.address, tokens100000) + await grt.connect(curator.signer).approve(gns.address, tokens100000) + await grt.connect(curator.signer).approve(curation.address, tokens100000) + await grt.connect(otherCurator.signer).approve(gns.address, tokens100000) + await grt.connect(otherCurator.signer).approve(curation.address, tokens100000) // Update curation tax to test the functionality of it in disableNameSignal() await curation.connect(governor.signer).setCurationTaxPercentage(curationTaxPercentage) }) @@ -616,212 +709,488 @@ describe('GNS', () => { describe('publishNewVersion', async function () { let subgraph: Subgraph - beforeEach(async () => { - subgraph = await publishNewSubgraph(me, newSubgraph0) - await mintSignal(me, subgraph.id, tokens10000) - }) + context('when minting happens after new version', async function () { + it('should publish a new version on an existing subgraph', async function () { + subgraph = await publishNewSubgraph(me, newSubgraph0) - it('should publish a new version on an existing subgraph', async function () { - await publishNewVersion(me, subgraph.id, newSubgraph1) - }) + await gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) - it('should publish a new version on an existing subgraph with no current signal', async function () { - const emptySignalSubgraph = await publishNewSubgraph(me, buildSubgraph()) - await publishNewVersion(me, emptySignalSubgraph.id, newSubgraph1) - }) + await gns.connect(me.signer).mintSignal(subgraph.id, tokens10000, 0) - it('should reject a new version with the same subgraph deployment ID', async function () { - const tx = gns - .connect(me.signer) - .publishNewVersion( - subgraph.id, - newSubgraph0.subgraphDeploymentID, - newSubgraph0.versionMetadata, - ) - await expect(tx).revertedWith( - 'GNS: Cannot publish a new version with the same subgraph deployment ID', - ) + // Versions after transaction + const [idOld, vSignalOld] = await gns.getSubgraphVersion(subgraph.id, 0) + const [idNew, vSignalNew] = await gns.getSubgraphVersion(subgraph.id, 1) + + // After state + const afterSubgraph = await gns.subgraphs(subgraph.id) + + // Check subgraph deployment IDs + expect(idOld).eq(newSubgraph0.subgraphDeploymentID) + expect(idNew).eq(newSubgraph1.subgraphDeploymentID) + + expect(afterSubgraph.subgraphDeploymentID).eq(newSubgraph1.subgraphDeploymentID) + }) }) - it('should reject publishing a version to a subgraph that does not exist', async function () { - const tx = gns - .connect(me.signer) - .publishNewVersion( - randomHexBytes(32), - newSubgraph1.subgraphDeploymentID, - newSubgraph1.versionMetadata, + context('when minting happens first', async function () { + beforeEach(async () => { + subgraph = await publishNewSubgraph(me, newSubgraph0) + await gns.connect(me.signer).mintSignal(subgraph.id, tokens10000, 0) + }) + + it('should publish a new version on an existing subgraph', async function () { + await publishNewVersion(me, subgraph.id, newSubgraph1) + }) + + it('should reject publishing a version to a subgraph that does not exist', async function () { + const tx = gns + .connect(me.signer) + .publishNewVersion( + randomHexBytes(32), + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + }) + + context('when new version already exists', async function () { + it('should publish a new version on an existing subgraph', async function () { + await publishNewVersion(me, subgraph.id, newSubgraph1) + await publishNewVersion( + me, + subgraph.id, + newSubgraph2, + 4515468750000000000000n, + 6719723766643983111n, + 13439447533287966222n, + 9746794344808963906n, + 9506250000000000000000n, + ) + }) + }) + + it('should reject a new version with the same subgraph deployment ID', async function () { + const tx = gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph0.subgraphDeploymentID, + newSubgraph0.versionMetadata, + ) + await expect(tx).revertedWith( + 'GNS: Cannot publish a new version with the same subgraph deployment ID', + ) + }) + + it('should reject publishing a version to a subgraph that does not exist', async function () { + const tx = gns + .connect(me.signer) + .publishNewVersion( + randomHexBytes(32), + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + }) + + it('reject if not the owner', async function () { + const tx = gns + .connect(other.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await expect(tx).revertedWith('GNS: Must be authorized') + }) + + it('should fail when upgrade tries to point to a pre-curated', async function () { + // Curate directly to the deployment + await curation.connect(me.signer).mint(newSubgraph1.subgraphDeploymentID, tokens1000, 0) + + // Target a pre-curated subgraph deployment + const tx = gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await expect(tx).revertedWith( + 'GNS: Owner cannot point to a subgraphID that has been pre-curated', ) - await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + }) + + it('should upgrade version when there is no signal with no signal migration', async function () { + const beforeUsersNSignal = await gns.getCuratorSignal(subgraph.id, me.address) + + gns.connect(me.signer).burnSignal(subgraph.id, beforeUsersNSignal, 0) + + const tx = gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await expect(tx) + .emit(gns, 'SubgraphVersionUpdated') + .withArgs(subgraph.id, newSubgraph1.subgraphDeploymentID, newSubgraph1.versionMetadata) + }) + + it('should fail when subgraph is deprecated', async function () { + await deprecateSubgraph(me, subgraph.id) + const tx = gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + // NOTE: deprecate burns the Subgraph NFT, when someone wants to publish a new version it won't find it + await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + }) + + it('should reject a new version with the same subgraph deployment ID', async function () { + const tx = gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph0.subgraphDeploymentID, + newSubgraph0.versionMetadata, + ) + await expect(tx).revertedWith( + 'GNS: Cannot publish a new version with the same subgraph deployment ID', + ) + }) }) - it('reject if not the owner', async function () { - const tx = gns - .connect(other.signer) - .publishNewVersion( - subgraph.id, - newSubgraph1.subgraphDeploymentID, - newSubgraph1.versionMetadata, - ) - await expect(tx).revertedWith('GNS: Must be authorized') + context('when no current signal', async function () { + it('should publish a new version on an existing subgraph', async function () { + const emptySignalSubgraph = await publishNewSubgraph(me, buildSubgraph()) + await publishNewVersion(me, emptySignalSubgraph.id, newSubgraph1, 0n, 0n, 0n, 0n) + }) }) + }) - it('should fail when upgrade tries to point to a pre-curated', async function () { - // Curate directly to the deployment - await curation.connect(me.signer).mint(newSubgraph1.subgraphDeploymentID, tokens1000, 0) + describe('finalizeSubgraphUpgrade', async function () { + context('when new version exists', async function () { + let subgraph: Subgraph - // Target a pre-curated subgraph deployment - const tx = gns - .connect(me.signer) - .publishNewVersion( - subgraph.id, - newSubgraph1.subgraphDeploymentID, - newSubgraph1.versionMetadata, - ) - await expect(tx).revertedWith( - 'GNS: Owner cannot point to a subgraphID that has been pre-curated', - ) - }) + beforeEach(async () => { + subgraph = await publishNewSubgraph(me, newSubgraph0) + await gns.connect(me.signer).mintSignal(subgraph.id, tokens10000, 0) - it('should upgrade version when there is no signal with no signal migration', async function () { - await burnSignal(me, subgraph.id) - const tx = gns - .connect(me.signer) - .publishNewVersion( - subgraph.id, - newSubgraph1.subgraphDeploymentID, - newSubgraph1.versionMetadata, - ) - await expect(tx) - .emit(gns, 'SubgraphVersionUpdated') - .withArgs(subgraph.id, newSubgraph1.subgraphDeploymentID, newSubgraph1.versionMetadata) + await gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + }) + + it('should finalize version', async function () { + const tokensExpectedOld = 8799375000000000000000n + const vSignalExpectedOld = 9380498387612462033n + + const tokensExpectedNew = 0n + const vSignalExpectedNew = 0n + + const nSignalExpected = 9746794344808963906n + const vSignalExpected = 9380498387612462033n + + // Finalized version + const [idFinalized] = await gns.getSubgraphVersion(subgraph.id, 0) + const [idLatest] = await gns.getSubgraphVersion(subgraph.id, 1) + + // Finalize + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraph.id) + + // Finalized veresion tokens and vSignal + const [tokensFinalized, vSignalFinalized] = await curationPoolTokensAndSignal(idFinalized) + + // Check finalized veresion + expect(toFloat(tokensFinalized)).eq(0) + expect(toFloat(vSignalFinalized)).eq(0) + + // Versions after transaction + const [idOld, vSignalOld] = await gns.getSubgraphVersion(subgraph.id, 0) + const [idNew, vSignalNew] = await gns.getSubgraphVersion(subgraph.id, 1) + + // After state + const afterSubgraph = await gns.subgraphs(subgraph.id) + const [afterTokensOld, afterVSignalOld] = await curationPoolTokensAndSignal(idOld) + const [afterTokensNew, afterVSignalNew] = await curationPoolTokensAndSignal(idNew) + + // Check old curation pool tokens and signal + expect(afterTokensOld).eq(BigNumber.from(tokensExpectedOld)) + expect(afterVSignalOld).eq(BigNumber.from(vSignalExpectedOld)) + + // Check new curation pool tokens and signal + expect(afterTokensNew).eq(BigNumber.from(tokensExpectedNew)) + expect(afterVSignalNew).eq(BigNumber.from(vSignalExpectedNew)) + + // Check GNS signal + expect(afterSubgraph.nSignal).eq(BigNumber.from(nSignalExpected)) + expect(afterSubgraph.vSignal).eq(BigNumber.from(vSignalExpected)) + + // Check subgraph deployment IDs + expect(idOld).eq(idLatest) + expect(idNew).eq('0x0000000000000000000000000000000000000000000000000000000000000000') + + // Check versions + expect(vSignalOld).eq(BigNumber.from(vSignalExpectedOld)) + expect(vSignalNew).eq(BigNumber.from(vSignalExpectedNew)) + }) }) - it('should fail when subgraph is deprecated', async function () { - await deprecateSubgraph(me, subgraph.id) - const tx = gns - .connect(me.signer) - .publishNewVersion( - subgraph.id, - newSubgraph1.subgraphDeploymentID, - newSubgraph1.versionMetadata, - ) - // NOTE: deprecate burns the Subgraph NFT, when someone wants to publish a new version it won't find it - await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + context('when new version does not exist', async function () { + let subgraph: Subgraph + + beforeEach(async () => { + subgraph = await publishNewSubgraph(me, newSubgraph0) + }) + + it('should revert', async function () { + const tx = gns.connect(me.signer).finalizeSubgraphUpgrade(subgraph.id) + + await expect(tx).revertedWith('GNS: New version does not exist') + }) }) }) describe('deprecateSubgraph', async function () { let subgraph: Subgraph - beforeEach(async () => { - subgraph = await publishNewSubgraph(me, newSubgraph0) - await mintSignal(me, subgraph.id, tokens10000) - }) + context('when new version exists', async function () { + beforeEach(async () => { + subgraph = await publishNewSubgraph(me, newSubgraph0) - it('should deprecate a subgraph', async function () { - await deprecateSubgraph(me, subgraph.id) - }) + await gns.connect(me.signer).mintSignal(subgraph.id, tokens10000, 0) - it('should prevent a deprecated subgraph from being republished', async function () { - await deprecateSubgraph(me, subgraph.id) - const tx = gns - .connect(me.signer) - .publishNewVersion( - subgraph.id, - newSubgraph1.subgraphDeploymentID, - newSubgraph1.versionMetadata, - ) - // NOTE: deprecate burns the Subgraph NFT, when someone wants to publish a new version it won't find it - await expect(tx).revertedWith('ERC721: owner query for nonexistent token') - }) + await gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + }) - it('reject if the subgraph does not exist', async function () { - const subgraphID = randomHexBytes(32) - const tx = gns.connect(me.signer).deprecateSubgraph(subgraphID) - await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + it('should deprecate a subgraph', async function () { + await deprecateSubgraph(me, subgraph.id) + }) }) - it('reject deprecate if not the owner', async function () { - const tx = gns.connect(other.signer).deprecateSubgraph(subgraph.id) - await expect(tx).revertedWith('GNS: Must be authorized') + context('when new version does not exist', async function () { + beforeEach(async () => { + subgraph = await publishNewSubgraph(me, newSubgraph0) + await gns.connect(me.signer).mintSignal(subgraph.id, tokens10000, 0) + }) + + it('should deprecate a subgraph', async function () { + await deprecateSubgraph(me, subgraph.id) + }) + + it('should prevent a deprecated subgraph from being republished', async function () { + await deprecateSubgraph(me, subgraph.id) + const tx = gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + // NOTE: deprecate burns the Subgraph NFT, when someone wants to publish a new version it won't find it + await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + }) + + it('reject if the subgraph does not exist', async function () { + const subgraphID = randomHexBytes(32) + const tx = gns.connect(me.signer).deprecateSubgraph(subgraphID) + await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + }) + + it('reject deprecate if not the owner', async function () { + const tx = gns.connect(other.signer).deprecateSubgraph(subgraph.id) + await expect(tx).revertedWith('GNS: Must be authorized') + }) + + it('should prevent a deprecated subgraph from being republished', async function () { + await deprecateSubgraph(me, subgraph.id) + const tx = gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + // NOTE: deprecate burns the Subgraph NFT, when someone wants to publish a new version it won't find it + await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + }) + + it('reject if the subgraph does not exist', async function () { + const subgraphID = randomHexBytes(32) + const tx = gns.connect(me.signer).deprecateSubgraph(subgraphID) + await expect(tx).revertedWith('ERC721: owner query for nonexistent token') + }) }) }) }) describe('Curating on names', async function () { describe('mintSignal()', async function () { - it('should deposit into the name signal curve', async function () { - const subgraph = await publishNewSubgraph(me, newSubgraph0) - await mintSignal(other, subgraph.id, tokens10000) + context('when new version already exists', async function () { + it('should deposit into the name signal curve', async function () { + const subgraph = await publishNewSubgraph(me, newSubgraph0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await mintSignal(other, subgraph.id, tokens10000, { + tokensOld: 4750000000000000000000n, + vSignalOld: 6892024376045110883n, + tokensNew: 4750000000000000000000n, + vSignalNew: 6892024376045110883n, + nSignal: 13784048752090221766n, + vSignal: 13784048752090221766n, + }) + }) }) - it('should fail when name signal is disabled', async function () { - const subgraph = await publishNewSubgraph(me, newSubgraph0) - await deprecateSubgraph(me, subgraph.id) - const tx = gns.connect(me.signer).mintSignal(subgraph.id, tokens1000, 0) - await expect(tx).revertedWith('GNS: Must be active') - }) - - it('should fail if you try to deposit on a non existing name', async function () { - const subgraphID = randomHexBytes(32) - const tx = gns.connect(me.signer).mintSignal(subgraphID, tokens1000, 0) - await expect(tx).revertedWith('GNS: Must be active') - }) - - it('reject minting if under slippage', async function () { - // First publish the subgraph - const subgraph = await publishNewSubgraph(me, newSubgraph0) - - // Set slippage to be 1 less than expected result to force reverting - const { 1: expectedNSignal } = await gns.tokensToNSignal(subgraph.id, tokens1000) - const tx = gns - .connect(me.signer) - .mintSignal(subgraph.id, tokens1000, expectedNSignal.add(1)) - await expect(tx).revertedWith('Slippage protection') + context('when new version does not exist', async function () { + it('should deposit into the name signal curve', async function () { + const subgraph = await publishNewSubgraph(me, newSubgraph0) + await mintSignal(other, subgraph.id, tokens10000) + }) + + it('should fail when name signal is disabled', async function () { + const subgraph = await publishNewSubgraph(me, newSubgraph0) + await deprecateSubgraph(me, subgraph.id) + const tx = gns.connect(me.signer).mintSignal(subgraph.id, tokens1000, 0) + await expect(tx).revertedWith('GNS: Must be active') + }) + + it('should fail if you try to deposit on a non existing name', async function () { + const subgraphID = randomHexBytes(32) + const tx = gns.connect(me.signer).mintSignal(subgraphID, tokens1000, 0) + await expect(tx).revertedWith('GNS: Must be active') + }) + + it('reject minting if under slippage', async function () { + // First publish the subgraph + const subgraph = await publishNewSubgraph(me, newSubgraph0) + + // Set slippage to be 1 less than expected result to force reverting + const { 1: expectedNSignal } = await gns.tokensToNSignal(subgraph.id, tokens1000) + const tx = gns + .connect(me.signer) + .mintSignal(subgraph.id, tokens1000, expectedNSignal.add(1)) + await expect(tx).revertedWith('Slippage protection') + }) }) }) describe('burnSignal()', async function () { let subgraph: Subgraph - beforeEach(async () => { - subgraph = await publishNewSubgraph(me, newSubgraph0) - await mintSignal(other, subgraph.id, tokens10000) - }) - - it('should withdraw from the name signal curve', async function () { - await burnSignal(other, subgraph.id) - }) - - it('should fail when name signal is disabled', async function () { - await deprecateSubgraph(me, subgraph.id) - // just test 1 since it will fail - const tx = gns.connect(me.signer).burnSignal(subgraph.id, 1, 0) - await expect(tx).revertedWith('GNS: Must be active') + context('when new version already exists', async function () { + beforeEach(async () => { + subgraph = await publishNewSubgraph(me, newSubgraph0) + + await gns.connect(other.signer).mintSignal(subgraph.id, tokens10000, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + }) + + it('should withdraw from the name signal curve', async function () { + await burnSignal(other, subgraph.id, { + vSignalEmit: 13610657588816199440n, + tokensEmit: 9262500000000000000000n, + }) + }) }) - it('should fail when the curator tries to withdraw more nSignal than they have', async function () { - const tx = gns.connect(me.signer).burnSignal( - subgraph.id, - // 1000000 * 10^18 nSignal is a lot, and will cause fail - toBN('1000000000000000000000000'), - 0, - ) - await expect(tx).revertedWith('GNS: Curator cannot withdraw more nSignal than they have') + context('when mint happens after new version already exists', async function () { + beforeEach(async () => { + subgraph = await publishNewSubgraph(me, newSubgraph0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraph.id, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(other.signer).mintSignal(subgraph.id, tokens10000, 0) + }) + + it('should withdraw from the name signal curve', async function () { + await burnSignal(other, subgraph.id, { + vSignalEmit: 13784048752090221766n, + }) + }) }) - it('reject burning if under slippage', async function () { - // Get current curator name signal - const curatorNSignal = await gns.getCuratorSignal(subgraph.id, other.address) - - // Withdraw - const { 1: expectedTokens } = await gns.nSignalToTokens(subgraph.id, curatorNSignal) - - // Force a revert by asking 1 more token than the function will return - const tx = gns - .connect(other.signer) - .burnSignal(subgraph.id, curatorNSignal, expectedTokens.add(1)) - await expect(tx).revertedWith('Slippage protection') + context('when new version does not exist', async function () { + beforeEach(async () => { + subgraph = await publishNewSubgraph(me, newSubgraph0) + await gns.connect(other.signer).mintSignal(subgraph.id, tokens10000, 0) + }) + + it('should withdraw from the name signal curve', async function () { + await burnSignal(other, subgraph.id) + }) + + it('should fail when name signal is disabled', async function () { + await deprecateSubgraph(me, subgraph.id) + // just test 1 since it will fail + const tx = gns.connect(me.signer).burnSignal(subgraph.id, 1, 0) + await expect(tx).revertedWith('GNS: Must be active') + }) + + it('should fail when the curator tries to withdraw more nSignal than they have', async function () { + const tx = gns.connect(me.signer).burnSignal( + subgraph.id, + // 1000000 * 10^18 nSignal is a lot, and will cause fail + toBN('1000000000000000000000000'), + 0, + ) + await expect(tx).revertedWith('GNS: Curator cannot withdraw more nSignal than they have') + }) + + it('reject burning if under slippage', async function () { + // Get current curator name signal + const curatorNSignal = await gns.getCuratorSignal(subgraph.id, other.address) + + // Withdraw + const { 1: expectedTokens } = await gns.nSignalToTokens(subgraph.id, curatorNSignal) + + // Force a revert by asking 1 more token than the function will return + const tx = gns + .connect(other.signer) + .burnSignal(subgraph.id, curatorNSignal, expectedTokens.add(1)) + await expect(tx).revertedWith('Slippage protection') + }) }) }) @@ -830,7 +1199,7 @@ describe('GNS', () => { beforeEach(async () => { subgraph = await publishNewSubgraph(me, newSubgraph0) - await mintSignal(other, subgraph.id, tokens10000) + await gns.connect(other.signer).mintSignal(subgraph.id, tokens10000, 0) }) it('should withdraw GRT from a disabled name signal', async function () { @@ -860,14 +1229,14 @@ describe('GNS', () => { describe('multiple minting', async function () { it('should mint less signal every time due to the bonding curve', async function () { const tokensToDepositMany = [ - toGRT('1000'), // should mint if we start with number above minimum deposit - toGRT('1000'), // every time it should mint less GCS due to bonding curve... - toGRT('1.06'), // should mint minimum deposit including tax - toGRT('1000'), - toGRT('1000'), - toGRT('2000'), - toGRT('2000'), + toGRT('10000'), // should mint if we start with number above minimum deposit + toGRT('10000'), // every time it should mint less GCS due to bonding curve... + toGRT('10000'), + toGRT('10000'), + toGRT('20000'), + toGRT('20000'), toGRT('123'), + toGRT('1'), // should mint minimum deposit including tax ] const subgraph = await publishNewSubgraph(me, newSubgraph0) @@ -886,10 +1255,13 @@ describe('GNS', () => { tokensToDeposit.sub(curationTax), beforeSubgraph.subgraphDeploymentID, ) - const tx = await mintSignal(me, subgraph.id, tokensToDeposit) + + const tx = await gns.connect(me.signer).mintSignal(subgraph.id, tokensToDeposit, 0) + const receipt = await tx.wait() const event: Event = receipt.events.pop() const nSignalCreated = event.args['nSignalCreated'] + expect(toRound(expectedNSignal)).eq(toRound(toFloat(nSignalCreated))) } }) @@ -901,21 +1273,34 @@ describe('GNS', () => { // note - reserve ratio is already set to 1000000 in GNS const tokensToDepositMany = [ - toGRT('1000'), // should mint if we start with number above minimum deposit - toGRT('1000'), // every time it should mint less GCS due to bonding curve... - toGRT('1000'), - toGRT('1000'), - toGRT('2000'), - toGRT('2000'), + toGRT('10000'), // should mint if we start with number above minimum deposit + toGRT('10000'), // every time it should mint less GCS due to bonding curve... + toGRT('10000'), + toGRT('10000'), + toGRT('20000'), + toGRT('20000'), toGRT('123'), toGRT('1'), // should mint below minimum deposit ] const subgraph = await publishNewSubgraph(me, newSubgraph0) + const expectedValues = [9500, 9500, 9500, 9500, 19000, 19000, 116.85, 0.95] - // State updated + let index = 0 for (const tokensToDeposit of tokensToDepositMany) { - await mintSignal(me, subgraph.id, tokensToDeposit) + const beforeSubgraph = await gns.subgraphs(subgraph.id) + + expect(newSubgraph0.subgraphDeploymentID).eq(beforeSubgraph.subgraphDeploymentID) + + const tx = await gns.connect(me.signer).mintSignal(subgraph.id, tokensToDeposit, 0) + + const receipt = await tx.wait() + const event: Event = receipt.events.pop() + const nSignalCreated = event.args['nSignalCreated'] + + expect(toFloat(nSignalCreated)).eq(expectedValues[index]) + + index++ } }) }) @@ -1006,4 +1391,809 @@ describe('GNS', () => { await expect(tx).revertedWith('') }) }) + + describe.only('Subgraph lifecycles scenarios', function () { + let subgraphID: any + + beforeEach(async () => { + subgraphID = buildSubgraphID(me.address, await gns.nextAccountSeqID(me.address)) + + await gns + .connect(me.signer) + .publishNewSubgraph( + newSubgraph0.subgraphDeploymentID, + newSubgraph0.versionMetadata, + newSubgraph0.subgraphMetadata, + ) + }) + + context('#1', async function () { + // publish new subgraph + // publish new version + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewSubgraph( + newSubgraph0.subgraphDeploymentID, + newSubgraph0.versionMetadata, + newSubgraph0.subgraphMetadata, + ) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#2', async function () { + // publish new subgraph + // publish new version + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewSubgraph( + newSubgraph0.subgraphDeploymentID, + newSubgraph0.versionMetadata, + newSubgraph0.subgraphMetadata, + ) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#3', async function () { + // publish new subgraph + // curator 1 - mint + // publish new version + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns + .connect(me.signer) + .publishNewSubgraph( + newSubgraph0.subgraphDeploymentID, + newSubgraph0.versionMetadata, + newSubgraph0.subgraphMetadata, + ) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#4', async function () { + // publish new subgraph + // publish new version + // curator 1 - mint + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#5', async function () { + // publish new subgraph + // publish new version + // finalize subgraph upgrade + // curator 1 - mint + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#6', async function () { + // publish new subgraph + // curator 1 - mint + // curator 1 - burn + // publish new version + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#7', async function () { + // publish new subgraph + // curator 1 - mint + // publish new version + // curator 1 - burn + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#8', async function () { + // publish new subgraph + // curator 1 - mint + // publish new version + // finalize subgraph upgrade + // curator 1 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#9', async function () { + // publish new subgraph + // publish new version + // curator 1 - mint + // curator 1 - burn + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#10', async function () { + // publish new subgraph + // publish new version + // curator 1 - mint + // finalize subgraph upgrade + // curator 1 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#11', async function () { + // publish new subgraph + // publish new version + // finalize subgraph upgrade + // curator 1 - mint + // curator 1 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#12', async function () { + // publish new subgraph + // curator 1 - mint + // curator 2 - mint + // curator 1 - burn + // curator 2 - burn + // publish new version + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#13', async function () { + // publish new subgraph + // curator 1 - mint + // curator 2 - mint + // publish new version + // curator 1 - burn + // curator 2 - burn + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#13a', async function () { + // publish new subgraph + // curator 1 - mint + // curator 2 - mint + // publish new version + // curator 1 - burn partially + // curator 2 - burn partially + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal.div(2), 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal.div(2), 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#13b', async function () { + // publish new subgraph + // curator 1 - mint + // curator 2 - mint + // publish new version + // curator 1 - burn + // curator 2 - burn + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + const tokens = toGRT('1000') + + await gns.connect(me.signer).mintSignal(subgraphID, tokens, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + let signal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, signal.div(2), 0) + + let i = 0 + while (i < 5) { + const signal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, signal.div(2), 0) + i++ + } + + signal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, signal, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#14', async function () { + // publish new subgraph + // curator 1 - mint + // curator 2 - mint + // publish new version + // finalize subgraph upgrade + // curator 1 - burn + // curator 2 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#15', async function () { + // publish new subgraph + // curator 1 - mint + // curator 1 - burn + // publish new version + // curator 2 - mint + // finalize subgraph upgrade + // curator 2 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#16', async function () { + // publish new subgraph + // curator 1 - mint + // curator 1 - burn + // publish new version + // finalize subgraph upgrade + // curator 2 - mint + // curator 2 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#17', async function () { + // publish new subgraph + // publish new version + // curator 1 - mint + // curator 2 - mint + // curator 1 - burn + // curator 2 - burn + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#18', async function () { + // publish new subgraph + // publish new version + // curator 1 - mint + // curator 2 - mint + // finalize subgraph upgrade + // curator 1 - burn + // curator 2 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#19', async function () { + // publish new subgraph + // publish new version + // curator 1 - mint + // curator 1 - burn + // finalize subgraph upgrade + // curator 2 - mint + // curator 2 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#20', async function () { + // publish new subgraph + // publish new version + // finalize subgraph upgrade + // curator 1 - mint + // curator 2 - mint + // curator 1 - burn + // curator 2 - burn + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + const beforeUsersNSignal = await gns.getCuratorSignal(subgraphID, me.address) + await gns.connect(me.signer).burnSignal(subgraphID, beforeUsersNSignal, 0) + + const beforeOtherNSignal = await gns.getCuratorSignal(subgraphID, other.address) + await gns.connect(other.signer).burnSignal(subgraphID, beforeOtherNSignal, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#21', async function () { + // publish new subgraph + // publish new version + // curator 1 - mint + // curator 2 - mint + // finalize subgraph upgrade + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + + context('#22', async function () { + // publish new subgraph + // publish new version + // finalize subgraph upgrade + // curator 1 - mint + // curator 2 - mint + // deprecate subgraph + + let subgraph: Subgraph + + it('should not revert', async function () { + await gns + .connect(me.signer) + .publishNewVersion( + subgraphID, + newSubgraph1.subgraphDeploymentID, + newSubgraph1.versionMetadata, + ) + + await gns.connect(me.signer).finalizeSubgraphUpgrade(subgraphID) + + await gns.connect(me.signer).mintSignal(subgraphID, tokens10000, 0) + await gns.connect(other.signer).mintSignal(subgraphID, tokens10000, 0) + + await gns.connect(me.signer).deprecateSubgraph(subgraphID) + }) + }) + }) }) diff --git a/tsconfig.json b/tsconfig.json index 1857a87d3..519ded40c 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,9 +1,9 @@ { "compilerOptions": { - "lib": ["ES2018", "dom"], + "lib": ["ES2020", "dom"], "module": "commonjs", "moduleResolution": "node", - "target": "ES2018", + "target": "ES2020", "outDir": "dist", "resolveJsonModule": true, "esModuleInterop": true