Skip to content

Commit 760a7ea

Browse files
committed
refactor: use composition instead of inheritance for the subgraph NFT and store IPFS metadata as token metadata
1 parent b9d3c48 commit 760a7ea

13 files changed

+320
-113
lines changed

contracts/base/SubgraphNFT.sol

Lines changed: 0 additions & 38 deletions
This file was deleted.

contracts/base/SubgraphNFTDescriptor.sol

Lines changed: 0 additions & 23 deletions
This file was deleted.

contracts/discovery/GNS.sol

Lines changed: 76 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ pragma solidity ^0.7.6;
44
pragma abicoder v2;
55

66
import "@openzeppelin/contracts/math/SafeMath.sol";
7+
import "@openzeppelin/contracts/utils/Address.sol";
78

89
import "../base/Multicall.sol";
9-
import "../base/SubgraphNFT.sol";
1010
import "../bancor/BancorFormula.sol";
1111
import "../upgrades/GraphUpgradeable.sol";
1212
import "../utils/TokenUtils.sol";
@@ -38,6 +38,8 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
3838

3939
// -- Events --
4040

41+
event SubgraphNFTUpdated(address subgraphNFT);
42+
4143
/**
4244
* @dev Emitted when graph account sets its default name
4345
*/
@@ -143,16 +145,16 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
143145
function initialize(
144146
address _controller,
145147
address _bondingCurve,
146-
address _tokenDescriptor
148+
address _subgraphNFT
147149
) external onlyImpl {
148150
Managed._initialize(_controller);
149151

150152
// Dependencies
151153
bondingCurve = _bondingCurve;
152-
__SubgraphNFT_init(_tokenDescriptor);
153154

154155
// Settings
155156
_setOwnerTaxPercentage(500000);
157+
_setSubgraphNFT(_subgraphNFT);
156158
}
157159

158160
/**
@@ -162,6 +164,8 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
162164
graphToken().approve(address(curation()), MAX_UINT256);
163165
}
164166

167+
// -- Config --
168+
165169
/**
166170
* @dev Set the owner fee percentage. This is used to prevent a subgraph owner to drain all
167171
* the name curators tokens while upgrading or deprecating and is configurable in parts per million.
@@ -171,14 +175,6 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
171175
_setOwnerTaxPercentage(_ownerTaxPercentage);
172176
}
173177

174-
/**
175-
* @dev Set the token descriptor contract.
176-
* @param _tokenDescriptor Address of the contract that creates the NFT token URI
177-
*/
178-
function setTokenDescriptor(address _tokenDescriptor) external override onlyGovernor {
179-
_setTokenDescriptor(_tokenDescriptor);
180-
}
181-
182178
/**
183179
* @dev Internal: Set the owner tax percentage. This is used to prevent a subgraph owner to drain all
184180
* the name curators tokens while upgrading or deprecating and is configurable in parts per million.
@@ -190,6 +186,29 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
190186
emit ParameterUpdated("ownerTaxPercentage");
191187
}
192188

189+
/**
190+
* @dev Set the NFT registry contract
191+
* @param _subgraphNFT Address of the ERC721 contract
192+
*/
193+
function setSubgraphNFT(address _subgraphNFT) public onlyGovernor {
194+
_setSubgraphNFT(_subgraphNFT);
195+
}
196+
197+
/**
198+
* @dev Internal: Set the NFT registry contract
199+
* @param _subgraphNFT Address of the ERC721 contract
200+
*/
201+
function _setSubgraphNFT(address _subgraphNFT) private {
202+
require(
203+
_subgraphNFT != address(0) && Address.isContract(_subgraphNFT),
204+
"NFT must be valid"
205+
);
206+
subgraphNFT = ISubgraphNFT(_subgraphNFT);
207+
emit SubgraphNFTUpdated(_subgraphNFT);
208+
}
209+
210+
// -- Actions --
211+
193212
/**
194213
* @dev Allows a graph account to set a default name
195214
* @param _graphAccount Account that is setting its name
@@ -217,6 +236,16 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
217236
override
218237
onlySubgraphAuth(_subgraphID)
219238
{
239+
_updateSubgraphMetadata(_subgraphID, _subgraphMetadata);
240+
}
241+
242+
/**
243+
* @dev Internal: Allows a subgraph owner to update the metadata of a subgraph they have published
244+
* @param _subgraphID Subgraph ID
245+
* @param _subgraphMetadata IPFS hash for the subgraph metadata
246+
*/
247+
function _updateSubgraphMetadata(uint256 _subgraphID, bytes32 _subgraphMetadata) internal {
248+
_setSubgraphURI(_subgraphID, string(abi.encodePacked(_subgraphMetadata)));
220249
emit SubgraphMetadataUpdated(_subgraphID, _subgraphMetadata);
221250
}
222251

@@ -241,12 +270,14 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
241270
subgraphData.subgraphDeploymentID = _subgraphDeploymentID;
242271
subgraphData.reserveRatio = defaultReserveRatio;
243272

244-
// Mint the NFT. Use the subgraphID as tokenId.
245-
// This function will check the if tokenId already exists.
246-
_mint(subgraphOwner, subgraphID);
247-
273+
// Mint the NFT. Use the subgraphID as tokenID.
274+
// This function will check the if tokenID already exists.
275+
_mintNFT(subgraphOwner, subgraphID);
248276
emit SubgraphPublished(subgraphID, _subgraphDeploymentID, defaultReserveRatio);
249-
emit SubgraphMetadataUpdated(subgraphID, _subgraphMetadata);
277+
278+
// Set the token metadata
279+
_updateSubgraphMetadata(subgraphID, _subgraphMetadata);
280+
250281
emit SubgraphVersionUpdated(subgraphID, _subgraphDeploymentID, _versionMetadata);
251282
}
252283

@@ -356,7 +387,7 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
356387
// subgraphData.subgraphDeploymentID = 0;
357388

358389
// Burn the NFT
359-
_burn(_subgraphID);
390+
_burnNFT(_subgraphID);
360391

361392
emit SubgraphDeprecated(_subgraphID, subgraphData.withdrawableGRT);
362393
}
@@ -637,21 +668,17 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
637668
return 0;
638669
}
639670

640-
/**
641-
* @dev Return the URI describing a particular token ID for a Subgraph.
642-
* @param _subgraphID Subgraph ID
643-
* @return The URI of the ERC721-compliant metadata
644-
*/
645-
function tokenURI(uint256 _subgraphID) public view override returns (string memory) {
646-
return tokenDescriptor.tokenURI(this, _subgraphID);
647-
}
648-
649671
/**
650672
* @dev Create subgraphID for legacy subgraph and mint ownership NFT.
651673
* @param _graphAccount Account that created the subgraph
652674
* @param _subgraphNumber The sequence number of the created subgraph
675+
* @param _subgraphMetadata IPFS hash for the subgraph metadata
653676
*/
654-
function migrateLegacySubgraph(address _graphAccount, uint256 _subgraphNumber) external {
677+
function migrateLegacySubgraph(
678+
address _graphAccount,
679+
uint256 _subgraphNumber,
680+
bytes32 _subgraphMetadata
681+
) external {
655682
// Must be an existing legacy subgraph
656683
bool legacySubgraphExists = legacySubgraphData[_graphAccount][_subgraphNumber]
657684
.subgraphDeploymentID != 0;
@@ -675,9 +702,11 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
675702

676703
// Mint the NFT and send to owner
677704
// The subgraph owner is the graph account that created it
678-
_mint(_graphAccount, subgraphID);
679-
705+
_mintNFT(_graphAccount, subgraphID);
680706
emit LegacySubgraphClaimed(_graphAccount, _subgraphNumber);
707+
708+
// Set the token metadata
709+
_updateSubgraphMetadata(subgraphID, _subgraphMetadata);
681710
}
682711

683712
/**
@@ -757,4 +786,22 @@ contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
757786
require(_isPublished(subgraphData) == true, "GNS: Must be active");
758787
return subgraphData;
759788
}
789+
790+
// -- NFT --
791+
792+
function ownerOf(uint256 _tokenID) public view override returns (address) {
793+
return subgraphNFT.ownerOf(_tokenID);
794+
}
795+
796+
function _mintNFT(address _owner, uint256 _tokenID) internal {
797+
subgraphNFT.mint(_owner, _tokenID);
798+
}
799+
800+
function _burnNFT(uint256 _tokenID) internal {
801+
subgraphNFT.burn(_tokenID);
802+
}
803+
804+
function _setSubgraphURI(uint256 _tokenID, string memory _subgraphURI) internal {
805+
subgraphNFT.setSubgraphURI(_tokenID, _subgraphURI);
806+
}
760807
}

contracts/discovery/GNSStorage.sol

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
pragma solidity ^0.7.6;
44
pragma abicoder v2;
55

6-
import "../base/SubgraphNFT.sol";
76
import "../governance/Managed.sol";
87

98
import "./erc1056/IEthereumDIDRegistry.sol";
109
import "./IGNS.sol";
10+
import "./ISubgraphNFT.sol";
1111

1212
abstract contract GNSV1Storage is Managed {
1313
// -- State --
@@ -37,12 +37,15 @@ abstract contract GNSV1Storage is Managed {
3737
IEthereumDIDRegistry private __DEPRECATED_erc1056Registry;
3838
}
3939

40-
abstract contract GNSV2Storage is GNSV1Storage, SubgraphNFT {
40+
abstract contract GNSV2Storage is GNSV1Storage {
4141
// Use it whenever a legacy (v1) subgraph NFT was claimed to maintain compatibility
4242
// Keep a reference from subgraphID => (graphAccount, subgraphNumber)
4343
mapping(uint256 => IGNS.LegacySubgraphKey) public legacySubgraphKeys;
4444

4545
// Store data for all NFT-based (v2) subgraphs
4646
// subgraphID => SubgraphData
4747
mapping(uint256 => IGNS.SubgraphData) public subgraphs;
48+
49+
// Contract that represents subgraph ownership through an NFT
50+
ISubgraphNFT public subgraphNFT;
4851
}

contracts/discovery/IGNS.sol

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ interface IGNS {
2626

2727
function setOwnerTaxPercentage(uint32 _ownerTaxPercentage) external;
2828

29-
function setTokenDescriptor(address _tokenDescriptor) external;
30-
3129
// -- Publishing --
3230

3331
function setDefaultName(
@@ -71,6 +69,8 @@ interface IGNS {
7169

7270
// -- Getters --
7371

72+
function ownerOf(uint256 _tokenID) external view returns (address);
73+
7474
function subgraphSignal(uint256 _subgraphID) external view returns (uint256);
7575

7676
function subgraphTokens(uint256 _subgraphID) external view returns (uint256);

contracts/discovery/ISubgraphNFT.sol

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// SPDX-License-Identifier: GPL-2.0-or-later
2+
3+
pragma solidity ^0.7.6;
4+
5+
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
6+
7+
interface ISubgraphNFT is IERC721 {
8+
// -- Config --
9+
10+
function setMinter(address _minter) external;
11+
12+
function setTokenDescriptor(address _tokenDescriptor) external;
13+
14+
function setBaseURI(string memory _baseURI) external;
15+
16+
// -- Actions --
17+
18+
function mint(address _to, uint256 _tokenId) external;
19+
20+
function burn(uint256 _tokenId) external;
21+
22+
function setSubgraphURI(uint256 _tokenId, string memory _subgraphURI) external;
23+
24+
function tokenURI(uint256 _tokenId) external view returns (string memory);
25+
}

contracts/base/ISubgraphNFTDescriptor.sol renamed to contracts/discovery/ISubgraphNFTDescriptor.sol

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
pragma solidity ^0.7.6;
44

5-
import "../discovery/IGNS.sol";
6-
75
/// @title Describes subgraph NFT tokens via URI
86
interface ISubgraphNFTDescriptor {
97
/// @notice Produces the URI describing a particular token ID for a Subgraph
108
/// @dev Note this URI may be a data: URI with the JSON contents directly inlined
11-
/// @param _gns GNS contract that holds the Subgraph data
12-
/// @param _subgraphID The ID of the subgraph NFT for which to produce a description, which may not be valid
9+
/// @param _minter Address of the allowed minter
10+
/// @param _tokenId The ID of the subgraph NFT for which to produce a description, which may not be valid
1311
/// @return The URI of the ERC721-compliant metadata
14-
function tokenURI(IGNS _gns, uint256 _subgraphID) external view returns (string memory);
12+
function tokenURI(
13+
address _minter,
14+
uint256 _tokenId,
15+
string calldata _baseURI,
16+
string calldata _tokenURI
17+
) external view returns (string memory);
1518
}

0 commit comments

Comments
 (0)