Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transferrable subgraph ownership #497

Merged
merged 19 commits into from
Dec 1, 2021
Merged

Transferrable subgraph ownership #497

merged 19 commits into from
Dec 1, 2021

Conversation

abarmat
Copy link
Contributor

@abarmat abarmat commented Aug 31, 2021

Motivation

App developers create subgraphs to index blockchain data. Then, they want indexers to run their subgraphs in the decentralized network. To achieve that, they publish a Subgraph in the GNS that targets a Subgraph Deployment. Once they publish a subgraph, they work to attract curators that delegate signal to the Subgraph so that the app developer can properly incentivize indexers.

For many reasons, the app developer might want to transfer ownership of the subgraph to a different account, a valid use case, not possible with the current implementation.

Specification

https://forum.thegraph.com/t/subgraph-ownership-transfer/2589

Copy link
Contributor

@davekaj davekaj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I like it overall, you are on the right path

_newSubgraphDeploymentID != namePool.subgraphDeploymentID,
"GNS: Cannot publish a new version with the same subgraph deployment ID"
);
// TODO: review this. why can't we allow new versions on non-curated ones?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was because the GNS used the Bancor Formula. So when we tried this upgrade, it would fail because we can't instantiate a Bancor curve with 0 tokens. We had curation enforce a minimum signal, but it didn't seem practical to have this as a requirement on GNS, since it would get the owner in a weird spot. It would need a minimum of Shares.

But we have an upgrade for the GNS to remove it from using the GNS. So, when that upgrade goes through, we can remove this requirement!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wisdom

uint256 tokensWithTax = _chargeOwnerTax(tokens, _graphAccount, curationTaxPercentage);
// Upgrade is only callable by the owner, we asume then that msg.sender = owner
address subgraphOwner = msg.sender;
uint256 tokensWithTax = _chargeOwnerTax(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we can rework the _changeOwnerTax() function. its implementation is awkward, as it tries to charge the owner the owner tax, but the curation tax eats into that. So we force the owner to pay slightly more, rather than the curators. its odd, I always felt like it could be better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, I would make it part of a different PR :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea

function mintNSignal(
address _graphAccount,
uint256 _subgraphNumber,
function mintSignal(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

everything was named with the N in it, such as mintNSignal, to distingush the different of minting on the GNS and minting on Curation. To help with the Subgraph and the front end for integrations.

Removing the N is okay with me. Just thought I'd mention it in case we want to keep a slight difference somehow. but it should be okay, as they are different contracts

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I tried to remove the N and V and make it all about subgraph and subgraph deployment, that is how I find we are calling the things in practice. In the Curation contract it is just plain mint()

namePool.reserveRatio = defaultReserveRatio;

emit SubgraphCreated(subgraphID, _subgraphDeploymentID, defaultReserveRatio);
emit SubgraphMetadataUpdated(subgraphID, _subgraphMetadata);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we call the function updateSubgraphMetadata() instead? Since all it does is emit the event

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I've just temporarily moved into a plain emit here to remove dependencies and see clearly what happens. I'll have as a note to re-use that function.

require(
_nSignal <= curatorNSignal,
"GNS: Curator cannot withdraw more nSignal than they have"
);

// Get tokens for name signal amount to burn
uint256 vSignal = nSignalToVSignal(_graphAccount, _subgraphNumber, _nSignal);
uint256 vSignal = nSignalToVSignal(_subgraphID, _nSignal);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe a bit odd we have nSignal and vSignal inside of the functions, but we have renamed events and functions.

i never liked the naming 😂 .maybe we can make it overall better and more understandable. no one in the community refers to nSignal or vSignal. its not a well understood concept. its just 'signal' to most people

Copy link
Contributor Author

@abarmat abarmat Aug 31, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can think of renaming, it's a bit confusing, the tricky thing is to find good names for this


// Every time an account creates a subgraph it increases a per-account sequence ID
// account => seqID
mapping(address => uint256) public nextAccountSeqID;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe accountSeqID or seqID will suffice, wdyt?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I did next was to make it clear that the number stored there is not the last one used but the next one that will be used when you create a subgraph, do you think there is a better name?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see.

I think there is probably a better name. I took 10 mins to try to think of one but I didn't come up with one. We can think about it while the PR is open, if nothing comes up im happy to use this name

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expectedAccountSeqID or similar?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

subsequentAccountId?

*/
function deprecateSubgraph(address _graphAccount, uint256 _subgraphNumber)
function deprecateSubgraph(uint256 _subgraphID)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will we burn the NFT here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was originally thinking about burning the NFT but I think we could just keep it there and be useless. The reason for that is that if we want to burn I need to get it transferred to the contract. I see no harm in the user keeping the NFT.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be worth while to think through what the Front end experience will be like.

If we are displaying NFTs, or someone goes to OpenSea and sees subgraph NFTs, it might be confusing.

Also if a subgraph developer has like 15 subgraphs, 8 of them deprecated, they might need to sift through them, like on etherscan.

maybe we burn, maybe we dont. lets think about it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decided to burn the token so it's not sitting there as something that someone can transfer but with no purpose to manage the subgraph.

No need to transfer the NFT to the GNS to burn it, we can use the internal _burn function because the GNS is the NFTRegistry.

    function _burn(uint256 tokenId) internal virtual {
        address owner = ERC721Upgradeable.ownerOf(tokenId); // internal owner

        _beforeTokenTransfer(owner, address(0), tokenId);

        // Clear approvals
        _approve(address(0), tokenId);

        // Clear metadata (if any)
        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }

        _holderTokens[owner].remove(tokenId);

        _tokenOwners.remove(tokenId);

        emit Transfer(owner, address(0), tokenId);
    }

namePool.disabled = true;

emit NameSignalDisabled(_graphAccount, _subgraphNumber, namePool.withdrawableGRT);
// TODO: review this function
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this in reference to the fact we will have to handle V1 subgraphs, as well as the NFT subgraphs?

Seems complex. But doable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_getSubgraphPool will give you the storage for the v1 subgraphs or the v2 subgraphs depending when it was created. We can't migrate old curators to the new struct.


// graphAccountID => subgraphNumber => NameCurationPool
mapping(address => mapping(uint256 => IGNS.NameCurationPool)) public nameSignals;
// TODO: use it whenever an "old" subgraph NFT was claimed to maintain compatibility
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this strategy will work for upgrading permissionlessly.

We could also consider that we just mint the NFTs on migration for our users. We could do it automatically, like prefill this data, or we could make it more permissionless by making it a function anyone can call.

The benefits are that we do not have to explain to the users how to do the migrate. I think a bit about the LEND --> AAVE migration. It is something that had to be explained, communicated, and had some split for a while on the two tokens. And then, even governance votes to close the migration contract, many months in the future.

But just an idea. it can be discussed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was thinking on making the claim function for the NFT public. Then to get it called for all existing subgraphs after an upgrade. That would initialize the required structures so anyone can call the contract functions with the new primary key.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like it

}

// TODO: claim an NFT and send it to the owner if not already done
function claimLegacy(address _graphAccount, uint256 _subgraphNumber) external {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to migrateLegacy

@abarmat abarmat marked this pull request as ready for review September 22, 2021 12:51
@abarmat abarmat added the GIP label Sep 22, 2021
@abarmat abarmat force-pushed the ariel/gns-ownership branch from f78723b to 9ef9062 Compare October 6, 2021 22:57
@abarmat
Copy link
Contributor Author

abarmat commented Oct 6, 2021

Rebased, squashed and clean up

@abarmat abarmat force-pushed the ariel/gns-ownership branch from 9ef9062 to f1f6624 Compare October 6, 2021 22:58
@codecov
Copy link

codecov bot commented Oct 6, 2021

Codecov Report

Merging #497 (2cf753f) into dev (6962037) will decrease coverage by 1.31%.
The diff coverage is 83.70%.

❗ Current head 2cf753f differs from pull request most recent head 1c15e48. Consider uploading reports for the commit 1c15e48 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##              dev     #497      +/-   ##
==========================================
- Coverage   91.91%   90.60%   -1.32%     
==========================================
  Files          33       35       +2     
  Lines        1658     1703      +45     
  Branches      282      282              
==========================================
+ Hits         1524     1543      +19     
- Misses        134      160      +26     
Flag Coverage Δ
unittests 90.60% <83.70%> (-1.32%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
contracts/base/SubgraphNFTDescriptor.sol 0.00% <0.00%> (ø)
contracts/discovery/GNS.sol 87.11% <84.12%> (-12.15%) ⬇️
contracts/base/SubgraphNFT.sol 100.00% <100.00%> (ø)
...ontracts/discovery/erc1056/EthereumDIDRegistry.sol 0.00% <0.00%> (-7.02%) ⬇️
contracts/curation/Curation.sol 100.00% <0.00%> (ø)
contracts/curation/GraphCurationToken.sol 100.00% <0.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 6962037...1c15e48. Read the comment docs.

- make the subgraph to use a single primary key
- major refactor of data structures
- refactor variable names and add migrate legacy subgraph
- get subgraph data depending if legacy or new subgraph
- improve validations
- refactor all subgraph checks
- update tests
- NFT burn and mint
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants