-
Notifications
You must be signed in to change notification settings - Fork 152
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
Conversation
There was a problem hiding this 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
contracts/discovery/GNS.sol
Outdated
_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? |
There was a problem hiding this comment.
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!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wisdom
contracts/discovery/GNS.sol
Outdated
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( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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( |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expectedAccountSeqID
or similar?
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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);
}
contracts/discovery/GNS.sol
Outdated
namePool.disabled = true; | ||
|
||
emit NameSignalDisabled(_graphAccount, _subgraphNumber, namePool.withdrawableGRT); | ||
// TODO: review this function |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
contracts/discovery/GNSStorage.sol
Outdated
|
||
// graphAccountID => subgraphNumber => NameCurationPool | ||
mapping(address => mapping(uint256 => IGNS.NameCurationPool)) public nameSignals; | ||
// TODO: use it whenever an "old" subgraph NFT was claimed to maintain compatibility |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i like it
contracts/discovery/GNS.sol
Outdated
} | ||
|
||
// TODO: claim an NFT and send it to the owner if not already done | ||
function claimLegacy(address _graphAccount, uint256 _subgraphNumber) external { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to migrateLegacy
f78723b
to
9ef9062
Compare
Rebased, squashed and clean up |
9ef9062
to
f1f6624
Compare
Codecov Report
@@ 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
Flags with carried forward coverage won't be shown. Click here to find out more.
Continue to review full report at Codecov.
|
- 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
4448e94
to
396d06b
Compare
Audit: [N03] Typographical and grammatical errors
Audit: [N05] Unnecessary use of msg.sender
Audit: [N01] Lack of input validation
Audit: [N01] Lack of input validation
Audit: [M01] Semantic overloading of approvals for access control
Audit: [X01] Subgraph deployment not updated when no signal
Audit: [X02] Update event name
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