22
33pragma solidity ^ 0.7.6 ;
44
5+ import "@openzeppelin/contracts/utils/Address.sol " ;
56import "@openzeppelin/contracts/math/SafeMath.sol " ;
7+ import "@openzeppelin/contracts/proxy/Clones.sol " ;
68
79import "../bancor/BancorFormula.sol " ;
810import "../upgrades/GraphUpgradeable.sol " ;
11+ import "../utils/TokenUtils.sol " ;
912
1013import "./CurationStorage.sol " ;
1114import "./ICuration.sol " ;
@@ -23,7 +26,7 @@ import "./GraphCurationToken.sol";
2326 * Holders can burn GCS using this contract to get GRT tokens back according to the
2427 * bonding curve.
2528 */
26- contract Curation is CurationV1Storage , GraphUpgradeable , ICuration {
29+ contract Curation is CurationV1Storage , GraphUpgradeable {
2730 using SafeMath for uint256 ;
2831
2932 // 100% in parts per million
@@ -70,6 +73,7 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
7073 function initialize (
7174 address _controller ,
7275 address _bondingCurve ,
76+ address _curationTokenMaster ,
7377 uint32 _defaultReserveRatio ,
7478 uint32 _curationTaxPercentage ,
7579 uint256 _minimumCurationDeposit
@@ -83,6 +87,7 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
8387 _setDefaultReserveRatio (_defaultReserveRatio);
8488 _setCurationTaxPercentage (_curationTaxPercentage);
8589 _setMinimumCurationDeposit (_minimumCurationDeposit);
90+ _setCurationTokenMaster (_curationTokenMaster);
8691 }
8792
8893 /**
@@ -154,10 +159,30 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
154159 "Curation tax percentage must be below or equal to MAX_PPM "
155160 );
156161
157- _curationTaxPercentage = _percentage;
162+ curationTaxPercentage = _percentage;
158163 emit ParameterUpdated ("curationTaxPercentage " );
159164 }
160165
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+
174+ /**
175+ * @dev Internal: Set the master copy to use as clones for the curation token.
176+ * @param _curationTokenMaster Address of implementation contract to use for curation tokens
177+ */
178+ function _setCurationTokenMaster (address _curationTokenMaster ) private {
179+ require (_curationTokenMaster != address (0 ), "Token master must be non-empty " );
180+ require (Address.isContract (_curationTokenMaster), "Token master must be a contract " );
181+
182+ curationTokenMaster = _curationTokenMaster;
183+ emit ParameterUpdated ("curationTokenMaster " );
184+ }
185+
161186 /**
162187 * @dev Assign Graph Tokens collected as curation fees to the curation pool reserve.
163188 * This function can only be called by the Staking contract and will do the bookeeping of
@@ -208,36 +233,27 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
208233
209234 // If it hasn't been curated before then initialize the curve
210235 if (! isCurated (_subgraphDeploymentID)) {
211- // Initialize
212236 curationPool.reserveRatio = defaultReserveRatio;
213237
214238 // If no signal token for the pool - create one
215239 if (address (curationPool.gcs) == address (0 )) {
216- // TODO: Use a minimal proxy to reduce gas cost
217- // https://github.com/graphprotocol/contracts/issues/405
218- // --abarmat-- 20201113
219- curationPool.gcs = IGraphCurationToken (
220- address (new GraphCurationToken (address (this )))
221- );
240+ // Use a minimal proxy to reduce gas cost
241+ IGraphCurationToken gcs = IGraphCurationToken (Clones.clone (curationTokenMaster));
242+ gcs.initialize (address (this ));
243+ curationPool.gcs = gcs;
222244 }
223245 }
224246
225247 // Trigger update rewards calculation snapshot
226248 _updateRewards (_subgraphDeploymentID);
227249
228250 // Transfer tokens from the curator to this contract
229- // This needs to happen after _updateRewards snapshot as that function
251+ // Burn the curation tax
252+ // NOTE: This needs to happen after _updateRewards snapshot as that function
230253 // is using balanceOf(curation)
231- IGraphToken graphToken = graphToken ();
232- require (
233- graphToken.transferFrom (curator, address (this ), _tokensIn),
234- "Cannot transfer tokens to deposit "
235- );
236-
237- // Burn withdrawal fees
238- if (curationTax > 0 ) {
239- graphToken.burn (curationTax);
240- }
254+ IGraphToken _graphToken = graphToken ();
255+ TokenUtils.pullTokens (_graphToken, curator, _tokensIn);
256+ TokenUtils.burnTokens (_graphToken, curationTax);
241257
242258 // Update curation pool
243259 curationPool.tokens = curationPool.tokens.add (_tokensIn.sub (curationTax));
@@ -284,13 +300,15 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
284300 curationPool.tokens = curationPool.tokens.sub (tokensOut);
285301 curationPool.gcs.burnFrom (curator, _signalIn);
286302
287- // 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
288305 if (getCurationPoolSignal (_subgraphDeploymentID) == 0 ) {
289- delete pools[_subgraphDeploymentID];
306+ curationPool.tokens = 0 ;
307+ curationPool.reserveRatio = 0 ;
290308 }
291309
292310 // Return the tokens to the curator
293- require (graphToken (). transfer (curator, tokensOut), " Error sending curator tokens " );
311+ TokenUtils. pushTokens (graphToken (), curator, tokensOut );
294312
295313 emit Burned (curator, _subgraphDeploymentID, tokensOut, _signalIn);
296314
@@ -318,10 +336,8 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
318336 override
319337 returns (uint256 )
320338 {
321- if (address (pools[_subgraphDeploymentID].gcs) == address (0 )) {
322- return 0 ;
323- }
324- return pools[_subgraphDeploymentID].gcs.balanceOf (_curator);
339+ IGraphCurationToken gcs = pools[_subgraphDeploymentID].gcs;
340+ return (address (gcs) == address (0 )) ? 0 : gcs.balanceOf (_curator);
325341 }
326342
327343 /**
@@ -335,10 +351,8 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
335351 override
336352 returns (uint256 )
337353 {
338- if (address (pools[_subgraphDeploymentID].gcs) == address (0 )) {
339- return 0 ;
340- }
341- return pools[_subgraphDeploymentID].gcs.totalSupply ();
354+ IGraphCurationToken gcs = pools[_subgraphDeploymentID].gcs;
355+ return (address (gcs) == address (0 )) ? 0 : gcs.totalSupply ();
342356 }
343357
344358 /**
@@ -355,14 +369,6 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
355369 return pools[_subgraphDeploymentID].tokens;
356370 }
357371
358- /**
359- * @dev Get curation tax percentage
360- * @return Amount the curation tax percentage in PPM
361- */
362- function curationTaxPercentage () external view override returns (uint32 ) {
363- return _curationTaxPercentage;
364- }
365-
366372 /**
367373 * @dev Calculate amount of signal that can be bought with tokens in a curation pool.
368374 * This function considers and excludes the deposit tax.
@@ -376,7 +382,7 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
376382 override
377383 returns (uint256 , uint256 )
378384 {
379- uint256 curationTax = _tokensIn.mul (uint256 (_curationTaxPercentage )).div (MAX_PPM);
385+ uint256 curationTax = _tokensIn.mul (uint256 (curationTaxPercentage )).div (MAX_PPM);
380386 uint256 signalOut = _tokensToSignal (_subgraphDeploymentID, _tokensIn.sub (curationTax));
381387 return (signalOut, curationTax);
382388 }
0 commit comments