Hey, I’m posting this new GIP that supersedes GIP-0018.
To make thing clear I kept most of the sections from the older GIP, updated what changed and added specific descriptions about the differences. Please see “Differences with GIP-0018”. I also provided some more description as to how metadata works.
GIP-0023 refactors the implementation of NFT ownership for a subgraph proposed in GIP-0018 by composing a separate NFT (ERC-721) contract instead of using the GNS as the registry.
The GNS contract allows anyone to publish a subgraph with associated metadata and a target subgraph deployment. The new Subgraph will then be tied forever to the account that created it. The impossibility of transferring the Subgraph ownership makes some use cases very inconvenient, such as moving your control to a multisig or a community member creating it on behalf of a DAO, etc. This proposal intend to solve those issues.
This GIP supersedes GIP-0018 maintaining the same goal but refactoring the implementation. All details about the changes are under the Specification section. Entire sections are copied over from the original GIP and updated to avoid misinterpretation.
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 which is not possible with the current implementation.
The proposed implementation involves a number of changes to make it easier to manage a subgraph. In addition to that, it simplifies many of the contract interfaces.
The current primary key that defines a subgraph is a combination of
subgraphNumber that gets passed to every function used to interact with the GNS.
To make it easier to store and reference, we will change that for a single
subgraphID will be determined at the moment of publication, and calculated like
seqIDis a sequential ID that gets incremented for that creator to avoid collisions
Whenever an app developer publishes a new subgraph, the GNS will mint an NFT. Whoever owns the NFT controls the subgraph. The NFT is based on a standard ERC721, so it can be easily transferred to different accounts with no limitation. Additionally, the NFT is burned when the owner deprecates the subgraph.
All access control for the following functions will be NFT-owner based:
The first implementation of the NFT-subgraph inherited the ERC721 behaviour from the GNS, and as a result, we could use the GNS contract as the registry. This brought a number of issues, the main one was that OpenSea, Etherscan and other apps would not detect the upgraded GNS as a valid ERC-721 NFT. The reason for that is that they scan for newly deployed contracts and “mark” them as non-fungible tokens. In addition to that, inheriting the full ERC721 code into the GNS ended up in bloated code in a single contract, getting close to the contract bytecode limit.
The new implementation proposed in this GIP uses a different NFT contract deployed separately from the GNS and make them work through composability.
To support this functionality we introduce two contracts:
SubgraphNFT: This is a standard ERC721 contract based from OpenZeppelin implementation. This contract uses a TokenDescriptor to render the tokenURI. The TokenDescriptor can be swapped by governance to provide future-proofing. The SubgraphNFT allows to set a special role called the
minterthat is the only one that can mint, burn or set the NFT metadata. The minter, in our setup, is the GNS.
SubgraphNFTDescriptor: This is a contract that implements the TokenDescriptor interface and whose only purpose is to render the tokenURI.
And we do the following changes to the GNS:
The GNS has an additional state variable that stores the SubgraphNFT address, so that whenever an app developer interacts with a subgraph, the GNS can mint, burn or check ownership of the subgraph through the NFT.
We leave the door open for Governance to swap the SubgraphNFT used by the GNS, but this should only be used on an emergency case and requires state migration.
The subgraph metadata is an IPFS-hash that contains a JSON file that encode relevant information about the subgraph, like an image, display name, category, etc.
The subgraph metadata was originally just emitted on the
SubgraphMetadataUpdated event whenever a subgraph was published or the app developer decided to update it.
This GIP propose to store the subgraph metadata (IPFS-hash) into a state variable in the SubgraphNFT. This way the NFT can then render the proper tokenURI out of it and be visible on wallets and any other NFT Marketplace.
A TokenDescriptor contract will be provided to convert the IPFS-hash format stored as a bytes32 into a compatible base58 string that IPFS uses in client URIs.
All functions and events where
subgraphNumber were passed will be changed for just a single
subgraphID. This is a breaking change for clients that will need to adapt to the new interface. The fact that the new subgraphID is the
keccak(graphAccount, subgraphNumber) makes it easier to translate old IDs into new ones.
The GNS included a feature to transfer the account identity that is not currently used and should be removed. For that it used the EthereumDIDRegistry contract.
The new implementation will expose a function that owners of old-type subgraphs can call to mint their NFTs. This function must ensure that it is only called once per old-type subgraph.
Additionally, the contract will keep track a mapping of
subgraphID => (graphAccount, subgraphNumber) for old subgraphs to make them backward compatible.
Performing this upgrade involves:
Migration of old subgraph types and minting of NFTs by calling a function exposed by the contract.
Any frontend that integrates GNS functionality needs to start using the single subgraphID.
Update the Core Network Subgraph to read the new events emitted by the contract.
It is recommended to warn any known dapp integrated with the contracts to ensure they update the interfaces.
- On the day of the upgrade the team needs to perform the following steps:
Deploy the new GNS implementation
Deploy the SubgraphNFTDescriptor
Deploy the SubgraphNFT
Verify all contracts on Etherscan
- The Council will then perform the following tasks if they agree on the upgrade:
Prepare the correct initializer parameters
Upgrade GNS setting the SubgraphNFT.address in the initializer when sending the
- The team will then call
migrateLegacy()function for each subgraph to mint the NFT on behalf of every user.
The proposal has a number of breaking changes.
Functions that change from taking
- updateSubgraphMetadata - publishNewSubgraph - deprecateSubgraph - mintNSignal -> mintSignal - burnNSignal -> burnSignal - withdraw - tokensToNSignal - nSignalToTokens - vSignalToNSignal - nSignalToVSignal - getCuratorNSignal -> getCuratorSignal - isPublished
The original implementation under GIP-0018 has been audited by OpenZeppelin (Report) and Consensys Diligence.
The refactored implementation related to this GIP was audited by Consensys Dilligence (PR-527 : Report)
All the audit reports can be found under the
/audit folder in GitHub - graphprotocol/contracts: The Graph Protocol.
Findings were fixed in the following commits:
- (3.1) →
- (3.2) →
The implementation has been deployed to Rinkeby multiple times. The team has practiced the upgrade and migration process using the following NPM packages.
Deployments of the newer version of the protocol:
- @graphprotocol/contracts/v/1.10.2-testnet-nft - @graphprotocol/contracts/v/1.10.1-testnet-nft - @graphprotocol/contracts/v/1.10.0-testnet-nft
Deployments of mainnet version of contracts then upgraded into the new ones:
- @graphprotocol/contracts/v/1.8.3-testnet-nft - @graphprotocol/contracts/v/1.8.2-testnet-nft - @graphprotocol/contracts/v/1.8.1-testnet-nft - @graphprotocol/contracts/v/1.8.0-testnet-nft
Copyright and related rights waived via CC0.