Skip to content

Commit ff07ea6

Browse files
committed
curation: add external function to set token master copy and avoid re-deploy the clone on minting reset
1 parent f6d56b9 commit ff07ea6

File tree

6 files changed

+65
-9
lines changed

6 files changed

+65
-9
lines changed

contracts/curation/Curation.sol

+12-5
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,14 @@ contract Curation is CurationV1Storage, GraphUpgradeable {
163163
emit ParameterUpdated("curationTaxPercentage");
164164
}
165165

166-
// TODO: add public version of this
166+
/**
167+
* @dev Set the master copy to use as clones for the curation token.
168+
* @param _curationTokenMaster Address of implementation contract to use for curation tokens
169+
*/
170+
function setCurationTokenMaster(address _curationTokenMaster) external override onlyGovernor {
171+
_setCurationTokenMaster(_curationTokenMaster);
172+
}
173+
167174
/**
168175
* @dev Internal: Set the master copy to use as clones for the curation token.
169176
* @param _curationTokenMaster Address of implementation contract to use for curation tokens
@@ -226,11 +233,9 @@ contract Curation is CurationV1Storage, GraphUpgradeable {
226233

227234
// If it hasn't been curated before then initialize the curve
228235
if (!isCurated(_subgraphDeploymentID)) {
229-
// Initialize
230236
curationPool.reserveRatio = defaultReserveRatio;
231237

232238
// If no signal token for the pool - create one
233-
// TODO: review if we can avoid re-deploying if was previously created
234239
if (address(curationPool.gcs) == address(0)) {
235240
// Use a minimal proxy to reduce gas cost
236241
IGraphCurationToken gcs = IGraphCurationToken(Clones.clone(curationTokenMaster));
@@ -295,9 +300,11 @@ contract Curation is CurationV1Storage, GraphUpgradeable {
295300
curationPool.tokens = curationPool.tokens.sub(tokensOut);
296301
curationPool.gcs.burnFrom(curator, _signalIn);
297302

298-
// If all signal burnt delete the curation pool
303+
// If all signal burnt delete the curation pool except for the
304+
// curation token contract to avoid recreating it on a new mint
299305
if (getCurationPoolSignal(_subgraphDeploymentID) == 0) {
300-
delete pools[_subgraphDeploymentID];
306+
curationPool.tokens = 0;
307+
curationPool.reserveRatio = 0;
301308
}
302309

303310
// Return the tokens to the curator

contracts/curation/GraphCurationToken.sol

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
77
import "../governance/Governed.sol";
88

99
/**
10-
* TODO: update to reflect that it is now a cloneable
1110
* @title GraphCurationToken contract
1211
* @dev This is the implementation of the Curation ERC20 token (GCS).
12+
*
1313
* GCS are created for each subgraph deployment curated in the Curation contract.
1414
* The Curation contract is the owner of GCS tokens and the only one allowed to mint or
1515
* burn them. GCS tokens are transferrable and their holders can do any action allowed
1616
* in a standard ERC20 token implementation except for burning them.
17+
*
18+
* This contract is meant to be used as the implementation for Minimal Proxy clones for
19+
* gas-saving purposes.
1720
*/
1821
contract GraphCurationToken is ERC20Upgradeable, Governed {
1922
/**

contracts/curation/ICuration.sol

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ interface ICuration {
1313

1414
function setCurationTaxPercentage(uint32 _percentage) external;
1515

16+
function setCurationTokenMaster(address _curationTokenMaster) external;
17+
1618
// -- Curation --
1719

1820
function mint(

contracts/governance/Managed.sol

+2-2
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,12 @@ contract Managed {
5656
require(msg.sender == address(controller), "Caller must be Controller");
5757
}
5858

59-
modifier notPartialPaused {
59+
modifier notPartialPaused() {
6060
_notPartialPaused();
6161
_;
6262
}
6363

64-
modifier notPaused {
64+
modifier notPaused() {
6565
_notPaused();
6666
_;
6767
}

test/curation/configuration.test.ts

+29-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { expect } from 'chai'
2+
import { constants } from 'ethers'
23

34
import { Curation } from '../../build/types/Curation'
45

56
import { defaults } from '../lib/deployment'
67
import { NetworkFixture } from '../lib/fixtures'
7-
import { getAccounts, toBN, Account } from '../lib/testHelpers'
8+
import { getAccounts, toBN, Account, randomAddress } from '../lib/testHelpers'
9+
10+
const { AddressZero } = constants
811

912
const MAX_PPM = 1000000
1013

@@ -99,4 +102,29 @@ describe('Curation:Config', () => {
99102
await expect(tx).revertedWith('Caller must be Controller governor')
100103
})
101104
})
105+
106+
describe('curationTokenMaster', function () {
107+
it('should set `curationTokenMaster`', async function () {
108+
const newCurationTokenMaster = curation.address
109+
await curation.connect(governor.signer).setCurationTokenMaster(newCurationTokenMaster)
110+
})
111+
112+
it('reject set `curationTokenMaster` to empty value', async function () {
113+
const newCurationTokenMaster = AddressZero
114+
const tx = curation.connect(governor.signer).setCurationTokenMaster(newCurationTokenMaster)
115+
await expect(tx).revertedWith('Token master must be non-empty')
116+
})
117+
118+
it('reject set `curationTokenMaster` to non-contract', async function () {
119+
const newCurationTokenMaster = randomAddress()
120+
const tx = curation.connect(governor.signer).setCurationTokenMaster(newCurationTokenMaster)
121+
await expect(tx).revertedWith('Token master must be a contract')
122+
})
123+
124+
it('reject set `curationTokenMaster` if not allowed', async function () {
125+
const newCurationTokenMaster = curation.address
126+
const tx = curation.connect(me.signer).setCurationTokenMaster(newCurationTokenMaster)
127+
await expect(tx).revertedWith('Caller must be Controller governor')
128+
})
129+
})
102130
})

test/curation/curation.test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,22 @@ describe('Curation', () => {
456456
.burn(subgraphDeploymentID, signalToRedeem, expectedTokens.add(1))
457457
await expect(tx).revertedWith('Slippage protection')
458458
})
459+
460+
it('should not re-deploy the curation token when signal is reset', async function () {
461+
const beforeSubgraphPool = await curation.pools(subgraphDeploymentID)
462+
463+
// Burn all the signal
464+
const signalToRedeem = await curation.getCuratorSignal(curator.address, subgraphDeploymentID)
465+
const expectedTokens = tokensToDeposit
466+
await shouldBurn(signalToRedeem, expectedTokens)
467+
468+
// Mint again on the same subgraph
469+
await curation.connect(curator.signer).mint(subgraphDeploymentID, tokensToDeposit, 0)
470+
471+
// Check state
472+
const afterSubgraphPool = await curation.pools(subgraphDeploymentID)
473+
expect(afterSubgraphPool.gcs).eq(beforeSubgraphPool.gcs)
474+
})
459475
})
460476

461477
describe('conservation', async function () {

0 commit comments

Comments
 (0)