-
Notifications
You must be signed in to change notification settings - Fork 1
/
GNS.sol
862 lines (746 loc) · 30.5 KB
/
GNS.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "../base/Multicall.sol";
import "../bancor/BancorFormula.sol";
import "../upgrades/GraphUpgradeable.sol";
import "../utils/TokenUtils.sol";
import "./IGNS.sol";
import "./GNSStorage.sol";
/**
* @title GNS
* @dev The Graph Name System contract provides a decentralized naming system for subgraphs
* used in the scope of the Graph Network. It translates Subgraphs into Subgraph Versions.
* Each version is associated with a Subgraph Deployment. The contract has no knowledge of
* human-readable names. All human readable names emitted in events.
* The contract implements a multicall behaviour to support batching multiple calls in a single
* transaction.
*/
contract GNS is GNSV2Storage, GraphUpgradeable, IGNS, Multicall {
using SafeMath for uint256;
// -- Constants --
uint256 private constant MAX_UINT256 = 2**256 - 1;
// 100% in parts per million
uint32 private constant MAX_PPM = 1000000;
// Equates to Connector weight on bancor formula to be CW = 1
uint32 private constant defaultReserveRatio = 1000000;
// -- Events --
event SubgraphNFTUpdated(address subgraphNFT);
/**
* @dev Emitted when graph account sets its default name
*/
event SetDefaultName(
address indexed graphAccount,
uint256 nameSystem, // only ENS for now
bytes32 nameIdentifier,
string name
);
/**
* @dev Emitted when the subgraph metadata is updated.
*/
event SubgraphMetadataUpdated(uint256 indexed subgraphID, bytes32 subgraphMetadata);
/**
* @dev Emitted when a subgraph version is updated.
*/
event SubgraphVersionUpdated(
uint256 indexed subgraphID,
bytes32 indexed subgraphDeploymentID,
bytes32 versionMetadata
);
/**
* @dev Emitted when a curator mints signal.
*/
event SignalMinted(
uint256 indexed subgraphID,
address indexed curator,
uint256 nSignalCreated,
uint256 vSignalCreated,
uint256 tokensDeposited
);
/**
* @dev Emitted when a curator burns signal.
*/
event SignalBurned(
uint256 indexed subgraphID,
address indexed curator,
uint256 nSignalBurnt,
uint256 vSignalBurnt,
uint256 tokensReceived
);
/**
* @dev Emitted when a curator transfers signal.
*/
event SignalTransferred(
uint256 indexed subgraphID,
address indexed from,
address indexed to,
uint256 nSignalTransferred
);
/**
* @dev Emitted when a subgraph is created.
*/
event SubgraphPublished(
uint256 indexed subgraphID,
bytes32 indexed subgraphDeploymentID,
uint32 reserveRatio
);
/**
* @dev Emitted when a subgraph is upgraded to point to a new
* subgraph deployment, burning all the old vSignal and depositing the GRT into the
* new vSignal curve.
*/
event SubgraphUpgraded(
uint256 indexed subgraphID,
uint256 vSignalCreated,
uint256 tokensSignalled,
bytes32 indexed subgraphDeploymentID
);
/**
* @dev Emitted when a subgraph is deprecated.
*/
event SubgraphDeprecated(uint256 indexed subgraphID, uint256 withdrawableGRT);
/**
* @dev Emitted when a curator withdraws GRT from a deprecated subgraph
*/
event GRTWithdrawn(
uint256 indexed subgraphID,
address indexed curator,
uint256 nSignalBurnt,
uint256 withdrawnGRT
);
// -- Modifiers --
/**
* @dev Emitted when a legacy subgraph is claimed
*/
event LegacySubgraphClaimed(address indexed graphAccount, uint256 subgraphNumber);
/**
* @dev Modifier that allows only a subgraph operator to be the caller
*/
modifier onlySubgraphAuth(uint256 _subgraphID) {
require(ownerOf(_subgraphID) == msg.sender, "GNS: Must be authorized");
_;
}
// -- Functions --
/**
* @dev Initialize this contract.
*/
function initialize(
address _controller,
address _bondingCurve,
address _subgraphNFT
) external onlyImpl {
Managed._initialize(_controller);
// Dependencies
bondingCurve = _bondingCurve;
// Settings
_setOwnerTaxPercentage(500000);
_setSubgraphNFT(_subgraphNFT);
}
/**
* @dev Approve curation contract to pull funds.
*/
function approveAll() external override {
graphToken().approve(address(curation()), MAX_UINT256);
}
// -- Config --
/**
* @dev Set the owner fee percentage. This is used to prevent a subgraph owner to drain all
* the name curators tokens while upgrading or deprecating and is configurable in parts per million.
* @param _ownerTaxPercentage Owner tax percentage
*/
function setOwnerTaxPercentage(uint32 _ownerTaxPercentage) external override onlyGovernor {
_setOwnerTaxPercentage(_ownerTaxPercentage);
}
/**
* @dev Internal: Set the owner tax percentage. This is used to prevent a subgraph owner to drain all
* the name curators tokens while upgrading or deprecating and is configurable in parts per million.
* @param _ownerTaxPercentage Owner tax percentage
*/
function _setOwnerTaxPercentage(uint32 _ownerTaxPercentage) private {
require(_ownerTaxPercentage <= MAX_PPM, "Owner tax must be MAX_PPM or less");
ownerTaxPercentage = _ownerTaxPercentage;
emit ParameterUpdated("ownerTaxPercentage");
}
/**
* @dev Set the NFT registry contract
* NOTE: Calling this function will break the ownership model unless
* it is replaced with a fully migrated version of the NFT contract state
* Use with care.
* @param _subgraphNFT Address of the ERC721 contract
*/
function setSubgraphNFT(address _subgraphNFT) public onlyGovernor {
_setSubgraphNFT(_subgraphNFT);
}
/**
* @dev Internal: Set the NFT registry contract
* @param _subgraphNFT Address of the ERC721 contract
*/
function _setSubgraphNFT(address _subgraphNFT) private {
require(_subgraphNFT != address(0), "NFT address cant be zero");
require(Address.isContract(_subgraphNFT), "NFT must be valid");
subgraphNFT = ISubgraphNFT(_subgraphNFT);
emit SubgraphNFTUpdated(_subgraphNFT);
}
// -- Actions --
/**
* @dev Allows a graph account to set a default name
* @param _graphAccount Account that is setting its name
* @param _nameSystem Name system account already has ownership of a name in
* @param _nameIdentifier The unique identifier that is used to identify the name in the system
* @param _name The name being set as default
*/
function setDefaultName(
address _graphAccount,
uint8 _nameSystem,
bytes32 _nameIdentifier,
string calldata _name
) external override {
require(_graphAccount == msg.sender, "GNS: Only you can set your name");
emit SetDefaultName(_graphAccount, _nameSystem, _nameIdentifier, _name);
}
/**
* @dev Allows a subgraph owner to update the metadata of a subgraph they have published
* @param _subgraphID Subgraph ID
* @param _subgraphMetadata IPFS hash for the subgraph metadata
*/
function updateSubgraphMetadata(uint256 _subgraphID, bytes32 _subgraphMetadata)
public
override
onlySubgraphAuth(_subgraphID)
{
_setSubgraphMetadata(_subgraphID, _subgraphMetadata);
}
/**
* @dev Publish a new subgraph.
* @param _subgraphDeploymentID Subgraph deployment for the subgraph
* @param _versionMetadata IPFS hash for the subgraph version metadata
* @param _subgraphMetadata IPFS hash for the subgraph metadata
*/
function publishNewSubgraph(
bytes32 _subgraphDeploymentID,
bytes32 _versionMetadata,
bytes32 _subgraphMetadata
) external override notPaused {
// Subgraph deployment must be non-empty
require(_subgraphDeploymentID != 0, "GNS: Cannot set deploymentID to 0 in publish");
// Init the subgraph
address subgraphOwner = msg.sender;
uint256 subgraphID = _nextSubgraphID(subgraphOwner);
SubgraphData storage subgraphData = _getSubgraphData(subgraphID);
subgraphData.subgraphDeploymentID = _subgraphDeploymentID;
subgraphData.reserveRatio = defaultReserveRatio;
// Mint the NFT. Use the subgraphID as tokenID.
// This function will check the if tokenID already exists.
_mintNFT(subgraphOwner, subgraphID);
emit SubgraphPublished(subgraphID, _subgraphDeploymentID, defaultReserveRatio);
// Set the token metadata
_setSubgraphMetadata(subgraphID, _subgraphMetadata);
emit SubgraphVersionUpdated(subgraphID, _subgraphDeploymentID, _versionMetadata);
}
/**
* @dev Publish a new version of an existing subgraph.
* @param _subgraphID Subgraph ID
* @param _subgraphDeploymentID Subgraph deployment ID of the new version
* @param _versionMetadata IPFS hash for the subgraph version metadata
*/
function publishNewVersion(
uint256 _subgraphID,
bytes32 _subgraphDeploymentID,
bytes32 _versionMetadata
) external override notPaused onlySubgraphAuth(_subgraphID) {
// Perform the upgrade from the current subgraph deployment to the new one.
// This involves burning all signal from the old deployment and using the funds to buy
// from the new deployment.
// This will also make the change to target to the new deployment.
// Subgraph check
SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID);
// New subgraph deployment must be non-empty
require(_subgraphDeploymentID != 0, "GNS: Cannot set deploymentID to 0 in publish");
// New subgraph deployment must be different than current
require(
_subgraphDeploymentID != subgraphData.subgraphDeploymentID,
"GNS: Cannot publish a new version with the same subgraph deployment ID"
);
// This is to prevent the owner from front running its name curators signal by posting
// its own signal ahead, bringing the name curators in, and dumping on them
ICuration curation = curation();
require(
!curation.isCurated(_subgraphDeploymentID),
"GNS: Owner cannot point to a subgraphID that has been pre-curated"
);
// 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,
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;
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);
emit SubgraphUpgraded(
_subgraphID,
subgraphData.vSignal,
tokensWithTax,
_subgraphDeploymentID
);
}
// Update target deployment
subgraphData.subgraphDeploymentID = _subgraphDeploymentID;
emit SubgraphVersionUpdated(_subgraphID, _subgraphDeploymentID, _versionMetadata);
}
/**
* @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.
* Can only be done by the subgraph owner.
* @param _subgraphID Subgraph ID
*/
function deprecateSubgraph(uint256 _subgraphID)
external
override
notPaused
onlySubgraphAuth(_subgraphID)
{
// Subgraph check
SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID);
// Burn signal only if it has any available
if (subgraphData.nSignal > 0) {
subgraphData.withdrawableGRT = curation().burn(
subgraphData.subgraphDeploymentID,
subgraphData.vSignal,
0
);
}
// 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
// subgraphData.subgraphDeploymentID = 0;
// Burn the NFT
_burnNFT(_subgraphID);
emit SubgraphDeprecated(_subgraphID, subgraphData.withdrawableGRT);
}
/**
* @dev Deposit GRT into a subgraph and mint signal.
* @param _subgraphID Subgraph ID
* @param _tokensIn The amount of tokens the nameCurator wants to deposit
* @param _nSignalOutMin Expected minimum amount of name signal to receive
*/
function mintSignal(
uint256 _subgraphID,
uint256 _tokensIn,
uint256 _nSignalOutMin
) external override notPartialPaused {
// Subgraph checks
SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID);
// Pull tokens from sender
address curator = msg.sender;
TokenUtils.pullTokens(graphToken(), curator, _tokensIn);
// Get name signal to mint for tokens deposited
(uint256 vSignal, ) = curation().mint(subgraphData.subgraphDeploymentID, _tokensIn, 0);
uint256 nSignal = vSignalToNSignal(_subgraphID, vSignal);
// Slippage protection
require(nSignal >= _nSignalOutMin, "GNS: Slippage protection");
// Update pools
subgraphData.vSignal = subgraphData.vSignal.add(vSignal);
subgraphData.nSignal = subgraphData.nSignal.add(nSignal);
subgraphData.curatorNSignal[curator] = subgraphData.curatorNSignal[curator].add(nSignal);
emit SignalMinted(_subgraphID, curator, nSignal, vSignal, _tokensIn);
}
/**
* @dev Burn signal for a subgraph and return the GRT.
* @param _subgraphID Subgraph ID
* @param _nSignal The amount of nSignal the nameCurator wants to burn
* @param _tokensOutMin Expected minimum amount of tokens to receive
*/
function burnSignal(
uint256 _subgraphID,
uint256 _nSignal,
uint256 _tokensOutMin
) external override notPartialPaused {
// Subgraph checks
SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID);
// Curator balance checks
address curator = msg.sender;
uint256 curatorNSignal = subgraphData.curatorNSignal[curator];
require(
_nSignal <= curatorNSignal,
"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);
// Update pools
subgraphData.vSignal = subgraphData.vSignal.sub(vSignal);
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);
}
/**
* @dev Move subgraph signal from sender to `_recipient`
* @param _subgraphID Subgraph ID
* @param _recipient Address to send the signal to
* @param _amount The amount of nSignal to transfer
*/
function transferSignal(
uint256 _subgraphID,
address _recipient,
uint256 _amount
) external override notPartialPaused {
require(_recipient != address(0), "GNS: Curator cannot transfer to the zero address");
// Subgraph checks
SubgraphData storage subgraphData = _getSubgraphOrRevert(_subgraphID);
// Balance checks
address curator = msg.sender;
uint256 curatorBalance = subgraphData.curatorNSignal[curator];
require(curatorBalance >= _amount, "GNS: Curator transfer amount exceeds balance");
// Move the signal
subgraphData.curatorNSignal[curator] = subgraphData.curatorNSignal[curator].sub(_amount);
subgraphData.curatorNSignal[_recipient] = subgraphData.curatorNSignal[_recipient].add(
_amount
);
emit SignalTransferred(_subgraphID, curator, _recipient, _amount);
}
/**
* @dev Withdraw tokens from a deprecated subgraph.
* When the subgraph is deprecated, any curator can call this function and
* withdraw the GRT they are entitled for its original deposit
* @param _subgraphID Subgraph ID
*/
function withdraw(uint256 _subgraphID) external override notPartialPaused {
// Subgraph validations
SubgraphData storage subgraphData = _getSubgraphData(_subgraphID);
require(subgraphData.disabled == true, "GNS: Must be disabled first");
require(subgraphData.withdrawableGRT > 0, "GNS: No more GRT to withdraw");
// Curator validations
address curator = msg.sender;
uint256 curatorNSignal = subgraphData.curatorNSignal[curator];
require(curatorNSignal > 0, "GNS: No signal to withdraw GRT");
// Get curator share of tokens to be withdrawn
uint256 tokensOut = curatorNSignal.mul(subgraphData.withdrawableGRT).div(
subgraphData.nSignal
);
subgraphData.curatorNSignal[curator] = 0;
subgraphData.nSignal = subgraphData.nSignal.sub(curatorNSignal);
subgraphData.withdrawableGRT = subgraphData.withdrawableGRT.sub(tokensOut);
// Return tokens to the curator
TokenUtils.pushTokens(graphToken(), curator, tokensOut);
emit GRTWithdrawn(_subgraphID, curator, curatorNSignal, tokensOut);
}
/**
* @dev Calculate tax that owner will have to cover for upgrading or deprecating.
* @param _tokens Tokens that were received from deprecating the old subgraph
* @param _owner Subgraph owner
* @param _curationTaxPercentage Tax percentage on curation deposits from Curation contract
* @return Total tokens that will be sent to curation, _tokens + ownerTax
*/
function _chargeOwnerTax(
uint256 _tokens,
address _owner,
uint32 _curationTaxPercentage
) private returns (uint256) {
if (_curationTaxPercentage == 0 || ownerTaxPercentage == 0) {
return 0;
}
// Tax on the total bonding curve funds
uint256 taxOnOriginal = _tokens.mul(_curationTaxPercentage).div(MAX_PPM);
// Total after the tax
uint256 totalWithoutOwnerTax = _tokens.sub(taxOnOriginal);
// The portion of tax that the owner will pay
uint256 ownerTax = taxOnOriginal.mul(ownerTaxPercentage).div(MAX_PPM);
uint256 totalWithOwnerTax = totalWithoutOwnerTax.add(ownerTax);
// The total after tax, plus owner partial repay, divided by
// the tax, to adjust it slightly upwards. ex:
// 100 GRT, 5 GRT Tax, owner pays 100% --> 5 GRT
// To get 100 in the protocol after tax, Owner deposits
// ~5.26, as ~105.26 * .95 = 100
uint256 totalAdjustedUp = totalWithOwnerTax.mul(MAX_PPM).div(
uint256(MAX_PPM).sub(uint256(_curationTaxPercentage))
);
uint256 ownerTaxAdjustedUp = totalAdjustedUp.sub(_tokens);
// Get the owner of the subgraph to reimburse the curation tax
TokenUtils.pullTokens(graphToken(), _owner, ownerTaxAdjustedUp);
return totalAdjustedUp;
}
/**
* @dev Calculate subgraph signal to be returned for an amount of tokens.
* @param _subgraphID Subgraph ID
* @param _tokensIn Tokens being exchanged for subgraph signal
* @return Amount of subgraph signal and curation tax
*/
function tokensToNSignal(uint256 _subgraphID, uint256 _tokensIn)
public
view
override
returns (
uint256,
uint256,
uint256
)
{
SubgraphData storage subgraphData = _getSubgraphData(_subgraphID);
(uint256 vSignal, uint256 curationTax) = curation().tokensToSignal(
subgraphData.subgraphDeploymentID,
_tokensIn
);
uint256 nSignal = vSignalToNSignal(_subgraphID, vSignal);
return (vSignal, nSignal, curationTax);
}
/**
* @dev Calculate tokens returned for an amount of subgraph signal.
* @param _subgraphID Subgraph ID
* @param _nSignalIn Subgraph signal being exchanged for tokens
* @return Amount of tokens returned for an amount of subgraph signal
*/
function nSignalToTokens(uint256 _subgraphID, uint256 _nSignalIn)
public
view
override
returns (uint256, uint256)
{
// 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);
return (vSignal, tokensOut);
}
/**
* @dev Calculate subgraph signal to be returned for an amount of subgraph deployment signal.
* @param _subgraphID Subgraph ID
* @param _vSignalIn Amount of subgraph deployment signal to exchange for subgraph signal
* @return Amount of subgraph signal that can be bought
*/
function vSignalToNSignal(uint256 _subgraphID, uint256 _vSignalIn)
public
view
override
returns (uint256)
{
SubgraphData storage subgraphData = _getSubgraphData(_subgraphID);
// Handle initialization by using 1:1 version to name signal
if (subgraphData.vSignal == 0) {
return _vSignalIn;
}
return
BancorFormula(bondingCurve).calculatePurchaseReturn(
subgraphData.nSignal,
subgraphData.vSignal,
subgraphData.reserveRatio,
_vSignalIn
);
}
/**
* @dev Calculate subgraph deployment signal to be returned for an amount of subgraph signal.
* @param _subgraphID Subgraph ID
* @param _nSignalIn Subgraph signal being exchanged for subgraph deployment signal
* @return Amount of subgraph deployment signal that can be returned
*/
function nSignalToVSignal(uint256 _subgraphID, uint256 _nSignalIn)
public
view
override
returns (uint256)
{
SubgraphData storage subgraphData = _getSubgraphData(_subgraphID);
return
BancorFormula(bondingCurve).calculateSaleReturn(
subgraphData.nSignal,
subgraphData.vSignal,
subgraphData.reserveRatio,
_nSignalIn
);
}
/**
* @dev Get the amount of subgraph signal a curator has.
* @param _subgraphID Subgraph ID
* @param _curator Curator address
* @return Amount of subgraph signal owned by a curator
*/
function getCuratorSignal(uint256 _subgraphID, address _curator)
public
view
override
returns (uint256)
{
return _getSubgraphData(_subgraphID).curatorNSignal[_curator];
}
/**
* @dev Return the total signal on the subgraph.
* @param _subgraphID Subgraph ID
* @return Total signal on the subgraph
*/
function subgraphSignal(uint256 _subgraphID) external view override returns (uint256) {
return _getSubgraphData(_subgraphID).nSignal;
}
/**
* @dev Return the total tokens on the subgraph at current value.
* @param _subgraphID Subgraph ID
* @return Total tokens on the subgraph
*/
function subgraphTokens(uint256 _subgraphID) external view override returns (uint256) {
uint256 signal = _getSubgraphData(_subgraphID).nSignal;
if (signal > 0) {
(, uint256 tokens) = nSignalToTokens(_subgraphID, signal);
return tokens;
}
return 0;
}
/**
* @dev Create subgraphID for legacy subgraph and mint ownership NFT.
* @param _graphAccount Account that created the subgraph
* @param _subgraphNumber The sequence number of the created subgraph
* @param _subgraphMetadata IPFS hash for the subgraph metadata
*/
function migrateLegacySubgraph(
address _graphAccount,
uint256 _subgraphNumber,
bytes32 _subgraphMetadata
) external {
// Must be an existing legacy subgraph
bool legacySubgraphExists = legacySubgraphData[_graphAccount][_subgraphNumber]
.subgraphDeploymentID != 0;
require(legacySubgraphExists == true, "GNS: Subgraph does not exist");
// Must not be a claimed subgraph
uint256 subgraphID = _buildSubgraphID(_graphAccount, _subgraphNumber);
require(
legacySubgraphKeys[subgraphID].account == address(0),
"GNS: Subgraph was already claimed"
);
// Store a reference for a legacy subgraph
legacySubgraphKeys[subgraphID] = IGNS.LegacySubgraphKey({
account: _graphAccount,
accountSeqID: _subgraphNumber
});
// Delete state for legacy subgraph
legacySubgraphs[_graphAccount][_subgraphNumber] = 0;
// Mint the NFT and send to owner
// The subgraph owner is the graph account that created it
_mintNFT(_graphAccount, subgraphID);
emit LegacySubgraphClaimed(_graphAccount, _subgraphNumber);
// Set the token metadata
_setSubgraphMetadata(subgraphID, _subgraphMetadata);
}
/**
* @dev Return whether a subgraph is published.
* @param _subgraphID Subgraph ID
* @return Return true if subgraph is currently published
*/
function isPublished(uint256 _subgraphID) public view override returns (bool) {
return _isPublished(_getSubgraphData(_subgraphID));
}
/**
* @dev Build a subgraph ID based on the account creating it and a sequence number for that account.
* Subgraph ID is the keccak hash of account+seqID
* @return Subgraph ID
*/
function _buildSubgraphID(address _account, uint256 _seqID) internal pure returns (uint256) {
return uint256(keccak256(abi.encodePacked(_account, _seqID)));
}
/**
* @dev Return the next subgraphID given the account that is creating the subgraph.
* NOTE: This function updates the sequence ID for the account
* @return Sequence ID for the account
*/
function _nextSubgraphID(address _account) internal returns (uint256) {
return _buildSubgraphID(_account, _nextAccountSeqID(_account));
}
/**
* @dev Return a new consecutive sequence ID for an account and update to the next value.
* NOTE: This function updates the sequence ID for the account
* @return Sequence ID for the account
*/
function _nextAccountSeqID(address _account) internal returns (uint256) {
uint256 seqID = nextAccountSeqID[_account];
nextAccountSeqID[_account] = nextAccountSeqID[_account].add(1);
return seqID;
}
/**
* @dev Get subgraph data.
* This function will first look for a v1 subgraph and return it if found.
* @param _subgraphID Subgraph ID
* @return Subgraph Data
*/
function _getSubgraphData(uint256 _subgraphID) private view returns (SubgraphData storage) {
// If there is a legacy subgraph created return it
LegacySubgraphKey storage legacySubgraphKey = legacySubgraphKeys[_subgraphID];
if (legacySubgraphKey.account != address(0)) {
return legacySubgraphData[legacySubgraphKey.account][legacySubgraphKey.accountSeqID];
}
// Return new subgraph type
return subgraphs[_subgraphID];
}
/**
* @dev Return whether a subgraph is published.
* @param _subgraphData Subgraph Data
* @return Return true if subgraph is currently published
*/
function _isPublished(SubgraphData storage _subgraphData) internal view returns (bool) {
return _subgraphData.subgraphDeploymentID != 0 && _subgraphData.disabled == false;
}
/**
* @dev Return the subgraph data or revert if not published or deprecated.
* @param _subgraphID Subgraph ID
* @return Subgraph Data
*/
function _getSubgraphOrRevert(uint256 _subgraphID)
internal
view
returns (SubgraphData storage)
{
SubgraphData storage subgraphData = _getSubgraphData(_subgraphID);
require(_isPublished(subgraphData) == true, "GNS: Must be active");
return subgraphData;
}
// -- NFT --
/**
* @dev Return the owner of a subgraph.
* @param _tokenID Subgraph ID
* @return Owner address
*/
function ownerOf(uint256 _tokenID) public view override returns (address) {
return subgraphNFT.ownerOf(_tokenID);
}
/**
* @dev Mint the NFT for the subgraph.
* @param _owner Owner address
* @param _tokenID Subgraph ID
*/
function _mintNFT(address _owner, uint256 _tokenID) internal {
subgraphNFT.mint(_owner, _tokenID);
}
/**
* @dev Burn the NFT for the subgraph.
* @param _tokenID Subgraph ID
*/
function _burnNFT(uint256 _tokenID) internal {
subgraphNFT.burn(_tokenID);
}
/**
* @dev Set the subgraph metadata.
* @param _tokenID Subgraph ID
* @param _subgraphMetadata IPFS hash of the subgraph metadata
*/
function _setSubgraphMetadata(uint256 _tokenID, bytes32 _subgraphMetadata) internal {
subgraphNFT.setSubgraphMetadata(_tokenID, _subgraphMetadata);
// Even if the following event is emitted in the NFT we emit it here to facilitate
// subgraph indexing
emit SubgraphMetadataUpdated(_tokenID, _subgraphMetadata);
}
}