This PR fixes an issue within StrategyProxy where externally claimed 3CRV is not detected:
Curve's FeeDistributor.claim method is unguarded - any address can trigger a claim for another address.
The current implementation of StrategyProxy relies on the return value from claim in order to know the size of the claim.
Thus, when a claim is triggered outside of StrategyProxy, the amount is not detected and not passed onto yveCRV.
Additionally, there is a claim performed on every single call. This is inefficient for two reasons:
Claiming only is possible once a week. The majority of these calls have no effect.
When it is possible to claim, the user epoch only advances by 50 points per claim. Due to he number of locking / extending actions performed each week, there are usually >50 points to advance. In the past week alone there were over 400.
How I did it
The logical flow introduced in this PR works as such:
FeeDistributor.time_cursor() is stored locally as lastTimeCursor. This variable is used within FeeDistributor to determine when new fees should be made claimable. At the start of each claim, if now < lastTimeCursor we can be certain there are no fees that can be claimed.
FeeDistributorcalculates claimable fees by iterating until last_token_time < time_cursor_of[acct]. We can claim in a loop until we see that this is condition is met, and be certain that all fees are claimed. By using claim_many we perform up to 20 "rounds" with each call.
Finally, we use 3CRV.balanceOf to get the actual available balance. This way, even fees that were claimed via a third-party call will be seen.
What I did
This PR fixes an issue within
StrategyProxy
where externally claimed 3CRV is not detected:FeeDistributor.claim
method is unguarded - any address can trigger a claim for another address.StrategyProxy
relies on the return value fromclaim
in order to know the size of the claim.StrategyProxy
, the amount is not detected and not passed ontoyveCRV
.Additionally, there is a claim performed on every single call. This is inefficient for two reasons:
How I did it
The logical flow introduced in this PR works as such:
FeeDistributor.time_cursor()
is stored locally aslastTimeCursor
. This variable is used withinFeeDistributor
to determine when new fees should be made claimable. At the start of each claim, ifnow < lastTimeCursor
we can be certain there are no fees that can be claimed.FeeDistributor
calculates claimable fees by iterating untillast_token_time < time_cursor_of[acct]
. We can claim in a loop until we see that this is condition is met, and be certain that all fees are claimed. By usingclaim_many
we perform up to 20 "rounds" with each call.3CRV.balanceOf
to get the actual available balance. This way, even fees that were claimed via a third-party call will be seen.