Skip to content

Commit f32ad12

Browse files
committed
Fix owner tax to incorporate small deviation of tax on tax
1 parent 2ee45d3 commit f32ad12

File tree

4 files changed

+68
-36
lines changed

4 files changed

+68
-36
lines changed

contracts/curation/Curation.sol

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,19 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
355355
return pools[_subgraphDeploymentID].tokens;
356356
}
357357

358+
/**
359+
* @dev Get curation tax percentage
360+
* @return Amount the curation tax percentage in PPM
361+
*/
362+
function getCurationTaxPercentage()
363+
external
364+
override
365+
view
366+
returns (uint32)
367+
{
368+
return curationTaxPercentage;
369+
}
370+
358371
/**
359372
* @dev Calculate amount of signal that can be bought with tokens in a curation pool.
360373
* This function considers and excludes the deposit tax.

contracts/curation/ICuration.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,6 @@ interface ICuration {
5959
external
6060
view
6161
returns (uint256);
62+
63+
function getCurationTaxPercentage() external view returns (uint32);
6264
}

contracts/discovery/GNS.sol

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
2323

2424
uint256 private constant MAX_UINT256 = 2**256 - 1;
2525

26+
// 100% in parts per million
27+
uint32 private constant MAX_PPM = 1000000;
28+
2629
// Equates to Connector weight on bancor formula to be CW = 1
2730
uint32 private constant defaultReserveRatio = 1000000;
2831

@@ -157,7 +160,7 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
157160
erc1056Registry = IEthereumDIDRegistry(_didRegistry);
158161

159162
// Settings
160-
_setOwnerTaxPercentage(50);
163+
_setOwnerTaxPercentage(500000);
161164
}
162165

163166
/**
@@ -182,7 +185,7 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
182185
* @param _ownerTaxPercentage Owner tax percentage
183186
*/
184187
function _setOwnerTaxPercentage(uint32 _ownerTaxPercentage) private {
185-
require(_ownerTaxPercentage <= 100, "Owner tax must be 100 or less");
188+
require(_ownerTaxPercentage <= MAX_PPM, "Owner tax must be MAX_PPM or less");
186189
ownerTaxPercentage = _ownerTaxPercentage;
187190
emit ParameterUpdated("ownerTaxPercentage");
188191
}
@@ -366,23 +369,20 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
366369
// Burn all version signal in the name pool for tokens
367370
uint256 tokens = curation.burn(namePool.subgraphDeploymentID, namePool.vSignal, 0);
368371

369-
// Take the owner cut of the curation tax
370-
(, uint256 curationTax) = curation.tokensToSignal(namePool.subgraphDeploymentID, tokens);
371-
uint256 ownerTax = _chargeOwnerTax(curationTax, _graphAccount);
372+
// Take the owner cut of the curation tax, add it to the total
373+
uint32 curationTaxPercentage = curation.getCurationTaxPercentage();
374+
375+
uint256 tokensWithTax = _chargeOwnerTax(tokens, _graphAccount, curationTaxPercentage);
372376

373377
// Update pool: constant nSignal, vSignal can change
374378
namePool.subgraphDeploymentID = _newSubgraphDeploymentID;
375-
(namePool.vSignal, ) = curation.mint(
376-
namePool.subgraphDeploymentID,
377-
tokens.add(ownerTax), // Cover part of the tax by the owner
378-
0
379-
);
379+
(namePool.vSignal, ) = curation.mint(namePool.subgraphDeploymentID, tokensWithTax, 0);
380380

381381
emit NameSignalUpgrade(
382382
_graphAccount,
383383
_subgraphNumber,
384384
namePool.vSignal,
385-
tokens.add(ownerTax),
385+
tokensWithTax,
386386
_newSubgraphDeploymentID
387387
);
388388
}
@@ -533,23 +533,35 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
533533

534534
/**
535535
* @dev Calculate tax that owner will have to cover for upgrading or deprecating.
536-
* @param _curationTax Total curation tax for changing subgraphs
536+
* @param _tokens Tokens that were received from deprecating the old subgraph
537537
* @param _owner Subgraph owner
538-
* @return Amount the owner must pay by transferring GRT to the GNS
539-
*/
540-
function _chargeOwnerTax(uint256 _curationTax, address _owner) private returns (uint256) {
541-
if (_curationTax == 0 || ownerTaxPercentage == 0) {
538+
* @param _curationTaxPercentage Tax percentage on curation deposits from Curation contract
539+
* @return Total tokens that will be sent to curation, _tokens + ownerTax
540+
*/
541+
function _chargeOwnerTax(
542+
uint256 _tokens,
543+
address _owner,
544+
uint32 _curationTaxPercentage
545+
) private returns (uint256) {
546+
if (_curationTaxPercentage == 0 || ownerTaxPercentage == 0) {
542547
return 0;
543548
}
544549

545-
uint256 ownerTax = _curationTax.mul(ownerTaxPercentage).div(100);
550+
// calculate tax on current tokens
551+
uint256 topUpTax = _tokens.mul(_curationTaxPercentage).div(MAX_PPM);
552+
// take full tax, and multiply it by owner percentage to
553+
// find the amount owner needs to add
554+
uint256 ownerTax = topUpTax.mul(ownerTaxPercentage).div(MAX_PPM);
546555

547556
// Get the owner of the subgraph to reimburse the curation tax
548557
require(
549558
graphToken().transferFrom(_owner, address(this), ownerTax),
550559
"GNS: Error reimbursing curation tax"
551560
);
552-
return ownerTax;
561+
562+
// new amount is the total tokens before plus the owner tax
563+
uint256 totalSentToCuration = _tokens.add(ownerTax);
564+
return totalSentToCuration;
553565
}
554566

555567
/**
@@ -565,8 +577,8 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
565577
uint256 _tokensIn
566578
)
567579
public
568-
override
569580
view
581+
override
570582
returns (
571583
uint256,
572584
uint256,
@@ -593,7 +605,7 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
593605
address _graphAccount,
594606
uint256 _subgraphNumber,
595607
uint256 _nSignalIn
596-
) public override view returns (uint256, uint256) {
608+
) public view override returns (uint256, uint256) {
597609
NameCurationPool storage namePool = nameSignals[_graphAccount][_subgraphNumber];
598610
uint256 vSignal = nSignalToVSignal(_graphAccount, _subgraphNumber, _nSignalIn);
599611
uint256 tokensOut = curation().signalToTokens(namePool.subgraphDeploymentID, vSignal);
@@ -611,7 +623,7 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
611623
address _graphAccount,
612624
uint256 _subgraphNumber,
613625
uint256 _vSignalIn
614-
) public override view returns (uint256) {
626+
) public view override returns (uint256) {
615627
NameCurationPool storage namePool = nameSignals[_graphAccount][_subgraphNumber];
616628

617629
// Handle initialization by using 1:1 version to name signal
@@ -639,7 +651,7 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
639651
address _graphAccount,
640652
uint256 _subgraphNumber,
641653
uint256 _nSignalIn
642-
) public override view returns (uint256) {
654+
) public view override returns (uint256) {
643655
NameCurationPool storage namePool = nameSignals[_graphAccount][_subgraphNumber];
644656
return
645657
BancorFormula(bondingCurve).calculateSaleReturn(
@@ -661,7 +673,7 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
661673
address _graphAccount,
662674
uint256 _subgraphNumber,
663675
address _curator
664-
) public override view returns (uint256) {
676+
) public view override returns (uint256) {
665677
return nameSignals[_graphAccount][_subgraphNumber].curatorNSignal[_curator];
666678
}
667679

@@ -673,8 +685,8 @@ contract GNS is GNSV1Storage, GraphUpgradeable, IGNS {
673685
*/
674686
function isPublished(address _graphAccount, uint256 _subgraphNumber)
675687
public
676-
override
677688
view
689+
override
678690
returns (bool)
679691
{
680692
return subgraphs[_graphAccount][_subgraphNumber] != 0;

test/gns.test.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ describe('GNS', () => {
168168
) => {
169169
// Before stats for the old vSignal curve
170170
const ownerTaxPercentage = await gns.ownerTaxPercentage()
171-
171+
const curationTaxPercentage = await curation.curationTaxPercentage()
172172
// Before stats for the name curve
173173
const namePoolBefore = await gns.nameSignals(graphAccount, subgraphNumber)
174174

@@ -178,15 +178,20 @@ describe('GNS', () => {
178178
subgraphNumber,
179179
namePoolBefore.nSignal,
180180
)
181-
182-
// Get the value for new vSignal that should be created on the new curve
183-
const { 1: curationTax } = await curation.tokensToSignal(
184-
subgraphToPublish.subgraphDeploymentID,
185-
tokensReceivedEstimate,
186-
)
187-
188-
// Since in upgrade, owner must refund part of curation tax, we need to actually add this back in
189-
const ownerTax = curationTax.mul(ownerTaxPercentage).div(toBN(100))
181+
// Example:
182+
// Deposit 100, 5 is taxed, 95 GRT in curve
183+
// Upgrade - calculate 5% tax on 95 --> 4.75 GRT
184+
// Multiple by ownerPercentage --> 50% * 4.75 = 2.375 GRT
185+
// Owner adds 2.375 to the amount, we deposit 97.375 GRT into the curve
186+
// In the end there will be 92.5 GRT left after the taxation of
187+
// the upgrade
188+
189+
// nSignalToTokens returns the amount of tokens with tax removed
190+
// already. So we must add in the tokens removed
191+
const MAX_PPM = 1000000
192+
const ownerFullTax = tokensReceivedEstimate.mul(curationTaxPercentage).div(MAX_PPM)
193+
194+
const ownerTax = ownerFullTax.mul(ownerTaxPercentage).div(MAX_PPM)
190195
const upgradeTokenReturn = tokensReceivedEstimate.add(ownerTax)
191196

192197
// Re-estimate amount of signal to get considering the owner tax paid by the owner
@@ -931,8 +936,8 @@ describe('GNS', () => {
931936
})
932937

933938
it('reject set `ownerTaxPercentage` if out of bounds', async function () {
934-
const tx = gns.connect(governor.signer).setOwnerTaxPercentage(101)
935-
await expect(tx).revertedWith('Owner tax must be 100 or less')
939+
const tx = gns.connect(governor.signer).setOwnerTaxPercentage(1000001)
940+
await expect(tx).revertedWith('Owner tax must be MAX_PPM or less')
936941
})
937942

938943
it('reject set `ownerTaxPercentage` if not allowed', async function () {

0 commit comments

Comments
 (0)