In cases when calling process_report(self)
on a vault with both a gain
and a non-zero total_refund
the vault total_idle
balance will be updated incorrectly.
In _process_report, in the case there is an accountant that returns some non-zero total_refunds, shares are minted (or not burned if there were shares to be burned) for that refund and are supposed to be locked.
# Get the amount we will lock to avoid a PPS increase.
if gain + total_refunds > 0 and profit_max_unlock_time != 0:
shares_to_lock = self._convert_to_shares(gain + total_refunds, Rounding.ROUND_DOWN)
Later, the refunds are pulled from the accountant and total_idle is updated given that we are minting new shares so they have to be backed by assets in total_idle to keep the pps constant (as the share are to be locked):
# Pull refunds
if total_refunds > 0:
# Transfer the refunded amount of asset to the vault.
self._erc20_safe_transfer_from(_asset, accountant, self, total_refunds)
# Update storage to increase total assets.
self.total_idle += total_refunds
However, if there were gain, total_idle is overwritten with total_assets which is the balance of the contract before pulling the refunds
# Record any reported gains.
if gain > 0:
# NOTE: this will increase total_assets
current_debt = unsafe_add(current_debt, gain)
if strategy != self:
self.strategies[strategy].current_debt = current_debt
self.total_debt += gain
else:
self.total_idle = total_assets
In this scenario total_idle
will not reflect the addition of the refunds, and the shares to lock will still be minted.
Note that a similar behaviour is present in the branch elif loss > 0.
Thanks to the ChainSec team for raising this issue.
Impact
- This bug was introduced in v3.0.3 and isolated to multi strategy vaults on that version only.
- The code path is only accessible on multi strategy vaults, and when the following conditions are true:
- an accountant is set
- the accountant issues a refund > 0
- the vault's asset balance does not match total_idle
- a call to
process_report(self)
is made
Patches
v3.0.4 will provide needed patches
Workarounds
- Do not issue refunds when reporting on self to accrue any airdropped total_idle
- Calling
process_report(self)
again with no refunds will cause the accounting to correct itself. Note: the corrective amounts may be locked and or have fees taken out depending on the vaults and vault accountants configuration.
In cases when calling
process_report(self)
on a vault with both again
and a non-zerototal_refund
the vaulttotal_idle
balance will be updated incorrectly.In _process_report, in the case there is an accountant that returns some non-zero total_refunds, shares are minted (or not burned if there were shares to be burned) for that refund and are supposed to be locked.
Later, the refunds are pulled from the accountant and total_idle is updated given that we are minting new shares so they have to be backed by assets in total_idle to keep the pps constant (as the share are to be locked):
However, if there were gain, total_idle is overwritten with total_assets which is the balance of the contract before pulling the refunds
In this scenario
total_idle
will not reflect the addition of the refunds, and the shares to lock will still be minted.Note that a similar behaviour is present in the branch elif loss > 0.
Thanks to the ChainSec team for raising this issue.
Impact
process_report(self)
is madePatches
v3.0.4 will provide needed patches
Workarounds
process_report(self)
again with no refunds will cause the accounting to correct itself. Note: the corrective amounts may be locked and or have fees taken out depending on the vaults and vault accountants configuration.