Skip to content

Commit f430146

Browse files
abarmatdavekay100
authored andcommitted
curation: remove totalTokens accounting and use balanceOf()
Initially we decided to use our own tracking of tokens used for curation to exclude tokens that might have been sent directly to the contract that could distort the rewards calculation. We had an issue in the totalTokens accounting that didn't account for the collected tokens. This lead to a mismatch in rewards proportional to total collected funds, but a bigger issue was that it could make the burn() function to fail as more tokens were in the contract than the expected in our own accounting. We find that there is no incentive to send tokens directly to the contract to distort those calculations and it is both more gas-efficient and safer to use balanceOf to get the signalled tokens from the Curation contract when querying from RewardsManager. This commit implements the use of balanceOf(Curation) from the RewardsManager to get totalTokens used for Curation.
1 parent d6f7789 commit f430146

File tree

6 files changed

+54
-68
lines changed

6 files changed

+54
-68
lines changed

contracts/curation/Curation.sol

Lines changed: 32 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,44 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
183183
// Need to deposit some funds
184184
require(_tokens > 0, "Cannot deposit zero tokens");
185185

186+
CurationPool storage curationPool = pools[_subgraphDeploymentID];
187+
188+
// If it hasn't been curated before then initialize the curve
189+
if (!isCurated(_subgraphDeploymentID)) {
190+
require(
191+
_tokens >= minimumCurationDeposit,
192+
"Curation deposit is below minimum required"
193+
);
194+
195+
// Initialize
196+
curationPool.reserveRatio = defaultReserveRatio;
197+
198+
// If no signal token for the pool - create one
199+
if (address(curationPool.gst) == address(0)) {
200+
// TODO: the gas cost of deploying the subgraph token can be greatly optimized
201+
// by deploying a proxy each time, sharing the same implementation
202+
curationPool.gst = IGraphSignalToken(
203+
address(new GraphSignalToken("GST", address(this)))
204+
);
205+
}
206+
}
207+
208+
// Trigger update rewards calculation snapshot
209+
_updateRewards(_subgraphDeploymentID);
210+
186211
// Transfer tokens from the curator to this contract
212+
// This needs to happen after _updateRewards snapshot as that function
213+
// is using balanceOf(curation)
187214
require(
188215
graphToken().transferFrom(curator, address(this), _tokens),
189216
"Cannot transfer tokens to deposit"
190217
);
191218

192-
// Deposit tokens to a curation pool reserve
193-
return _mint(curator, _subgraphDeploymentID, _tokens);
219+
// Exchange GRT tokens for GST of the subgraph pool
220+
uint256 signal = _mintSignal(curator, _subgraphDeploymentID, _tokens);
221+
222+
emit Signalled(curator, _subgraphDeploymentID, _tokens, signal);
223+
return signal;
194224
}
195225

196226
/**
@@ -396,9 +426,6 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
396426
// Mint signal to the curator
397427
curationPool.gst.mint(_curator, signal);
398428

399-
// Update the global reserve
400-
totalTokens = totalTokens.add(_tokens);
401-
402429
return signal;
403430
}
404431

@@ -424,9 +451,6 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
424451
// Burn signal from curator
425452
curationPool.gst.burnFrom(_curator, _signal);
426453

427-
// Update the global reserve
428-
totalTokens = totalTokens.sub(outTokens);
429-
430454
return (tokens, withdrawalFees);
431455
}
432456

@@ -448,50 +472,6 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
448472
emit Collected(_subgraphDeploymentID, _tokens);
449473
}
450474

451-
/**
452-
* @dev Deposit Graph Tokens in exchange for signal of a curation pool.
453-
* @param _curator Address of the staking party
454-
* @param _subgraphDeploymentID Subgraph deployment from where the curator is minting
455-
* @param _tokens Amount of Graph Tokens to deposit
456-
* @return Signal minted
457-
*/
458-
function _mint(
459-
address _curator,
460-
bytes32 _subgraphDeploymentID,
461-
uint256 _tokens
462-
) internal returns (uint256) {
463-
CurationPool storage curationPool = pools[_subgraphDeploymentID];
464-
465-
// If it hasn't been curated before then initialize the curve
466-
if (!isCurated(_subgraphDeploymentID)) {
467-
require(
468-
_tokens >= minimumCurationDeposit,
469-
"Curation deposit is below minimum required"
470-
);
471-
472-
// Initialize
473-
curationPool.reserveRatio = defaultReserveRatio;
474-
475-
// If no signal token for the pool - create one
476-
if (address(curationPool.gst) == address(0)) {
477-
// TODO: the gas cost of deploying the subgraph token can be greatly optimized
478-
// by deploying a proxy each time, sharing the same implementation
479-
curationPool.gst = IGraphSignalToken(
480-
address(new GraphSignalToken("GST", address(this)))
481-
);
482-
}
483-
}
484-
485-
// Trigger update rewards calculation
486-
_updateRewards(_subgraphDeploymentID);
487-
488-
// Exchange GRT tokens for GST of the subgraph pool
489-
uint256 signal = _mintSignal(_curator, _subgraphDeploymentID, _tokens);
490-
491-
emit Signalled(_curator, _subgraphDeploymentID, _tokens, signal);
492-
return signal;
493-
}
494-
495475
/**
496476
* @dev Triggers an update of rewards due to a change in signal.
497477
* @param _subgraphDeploymentID Subgrapy deployment updated
@@ -503,11 +483,4 @@ contract Curation is CurationV1Storage, GraphUpgradeable, ICuration {
503483
}
504484
return 0;
505485
}
506-
507-
/**
508-
* @dev Exter
509-
*/
510-
function getTotalTokens() external override view returns (uint256) {
511-
return totalTokens;
512-
}
513486
}

contracts/curation/CurationStorage.sol

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ contract CurationV1Storage is Managed {
1818
// This is the `startPoolBalance` for the bonding curve
1919
uint256 public minimumCurationDeposit;
2020

21-
// Total tokens in held as reserves received from curators deposits
22-
uint256 internal totalTokens;
23-
2421
// Bonding curve formula
2522
address public bondingCurve;
2623

contracts/curation/ICuration.sol

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@ interface ICuration {
1313

1414
// -- Configuration --
1515

16-
function getTotalTokens() external view returns (uint256);
17-
1816
function setDefaultReserveRatio(uint32 _defaultReserveRatio) external;
1917

2018
function setMinimumCurationDeposit(uint256 _minimumCurationDeposit) external;

contracts/rewards/RewardsManager.sol

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ contract RewardsManager is RewardsManagerV1Storage, GraphUpgradeable, IRewardsMa
165165
* @return newly accrued rewards per signal since last update
166166
*/
167167
function getNewRewardsPerSignal() public override view returns (uint256) {
168+
IGraphToken graphToken = graphToken();
169+
168170
// Calculate time steps
169171
uint256 t = block.number.sub(accRewardsPerSignalLastBlockUpdated);
170172
// Optimization to skip calculations if zero time steps elapsed
@@ -178,16 +180,16 @@ contract RewardsManager is RewardsManagerV1Storage, GraphUpgradeable, IRewardsMa
178180
}
179181

180182
// Zero issuance if no signalled tokens
181-
uint256 signalledTokens = curation().getTotalTokens();
183+
uint256 signalledTokens = graphToken.balanceOf(address(curation()));
182184
if (signalledTokens == 0) {
183185
return 0;
184186
}
185187

186188
uint256 r = issuanceRate;
187-
uint256 p = graphToken().totalSupply();
189+
uint256 p = graphToken.totalSupply();
188190
uint256 a = p.mul(_pow(r, t, TOKEN_DECIMALS)).div(TOKEN_DECIMALS);
189191

190-
// New issuance per signal during time steps
192+
// New issuance per signalled token during time steps
191193
uint256 x = a.sub(p);
192194

193195
// We multiply the decimals to keep the precision as fixed-point number

test/curation/curation.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,22 @@ describe('Curation', () => {
324324
await shouldCollect(toGRT('200'))
325325
await shouldCollect(toGRT('500.25'))
326326
})
327+
328+
it('should collect tokens and then unsignal all', async function () {
329+
await controller
330+
.connect(governor.signer)
331+
.setContractProxy(utils.id('Staking'), stakingMock.address)
332+
333+
// Collect increase the pool reserves
334+
await shouldCollect(toGRT('100'))
335+
336+
// When we burn signal we should get more tokens than initially curated
337+
const signalToRedeem = await curation.getCuratorSignal(
338+
curator.address,
339+
subgraphDeploymentID,
340+
)
341+
await shouldRedeem(signalToRedeem, toGRT('1100'))
342+
})
327343
})
328344
})
329345

test/rewards/rewards.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ describe('Rewards', () => {
7878
async snapshot() {
7979
this.accumulated = this.accumulated.add(await this.accruedGRT())
8080
this.totalSupply = await grt.totalSupply()
81-
this.totalSignalled = await curation.getTotalTokens()
81+
this.totalSignalled = await grt.balanceOf(curation.address)
8282
this.lastUpdatedBlock = await latestBlock()
8383
return this
8484
}

0 commit comments

Comments
 (0)