TWAPOracle price can be manipulated by depositing / withdrawing from the underlying pool
Summary
The LibTWAPOracle bases the TWAP price for Ubiquity dollar on a curve meta pool for 3CRV / Ubiquity dollar. The currently deployed version of that pool only holds below 27k$ of 3CRV.
Anyone can manipulate the price of Ubiquity dollar reflected by that pool easily with little investment.
Vulnerability Detail
The function to update the price of Ubiquity dollar and 3CRV calls pool.get_dy() with the twap balances.
uint256[2] memory twapBalances = IMetaPool(ts.pool)
.get_twap_balances(
ts.priceCumulativeLast,
priceCumulative,
blockTimestamp - ts.pricesBlockTimestampLast
);
// price to exchange amountIn Ubiquity Dollar to 3CRV based on TWAP
ts.price0Average = IMetaPool(ts.pool).get_dy(
0,
1,
1 ether,
twapBalances
);
get_dy() gets the amount of CRV out for 1 ether of Ubiquity dollar in. get_twap_balances() gets the average balance across two cumulative prices:
def get_twap_balances(_first_balances: uint256[N_COINS], _last_balances: uint256[N_COINS], _time_elapsed: uint256) -> uint256[N_COINS]:
balances: uint256[N_COINS] = empty(uint256[N_COINS])
for x in range(N_COINS):
balances[x] = (_last_balances[x] - _first_balances[x]) / _time_elapsed
return balances
The cumulative prices are fetched by LibTWAPOracle via currentCumulativePrices():
@internal
def _update():
"""
Commits pre-change balances for the previous block
Can be used to compare against current values for flash loan checks
"""
elapsed_time: uint256 = block.timestamp - self.block_timestamp_last
if elapsed_time > 0:
for i in range(N_COINS):
_balance: uint256 = self.balances[i]
self.price_cumulative_last[i] += _balance * elapsed_time
self.previous_balances[i] = _balance
self.block_timestamp_last = block.timestamp
This purely takes into account the current balance in token of the contract multiplied by the elapsed time. This balance can easily be manipulated by depositing / withdrawing little amounts from the metapool.
Impact
The Ubiquity dollar price in 3CRV (in this protocol taken as the USD price of Ubiquity dollar) can be easily manipulated with very little investment.
This price is used to enforce when minting / redeeming Ubiquity is allowed in LibUbiquityPool.sol and these enforcement no longer hold when price is manipulated.
cducrest-brainbot
high
TWAPOracle price can be manipulated by depositing / withdrawing from the underlying pool
Summary
The LibTWAPOracle bases the TWAP price for Ubiquity dollar on a curve meta pool for 3CRV / Ubiquity dollar. The currently deployed version of that pool only holds below 27k$ of 3CRV.
Anyone can manipulate the price of Ubiquity dollar reflected by that pool easily with little investment.
Vulnerability Detail
The function to update the price of Ubiquity dollar and 3CRV calls
pool.get_dy()
with the twap balances.get_dy()
gets the amount of CRV out for 1 ether of Ubiquity dollar in.get_twap_balances()
gets the average balance across two cumulative prices:The cumulative prices are fetched by
LibTWAPOracle
viacurrentCumulativePrices()
:This calls on the metapool:
The value of
price_cumulative_last
is updated in:This purely takes into account the current balance in token of the contract multiplied by the elapsed time. This balance can easily be manipulated by depositing / withdrawing little amounts from the metapool.
Impact
The Ubiquity dollar price in 3CRV (in this protocol taken as the USD price of Ubiquity dollar) can be easily manipulated with very little investment.
This price is used to enforce when minting / redeeming Ubiquity is allowed in
LibUbiquityPool.sol
and these enforcement no longer hold when price is manipulated.Code Snippet
https://github.com/sherlock-audit/2023-12-ubiquity/blob/main/ubiquity-dollar/packages/contracts/src/dollar/libraries/LibTWAPOracle.sol#L68-L102
https://github.com/sherlock-audit/2023-12-ubiquity/blob/main/ubiquity-dollar/packages/contracts/src/dollar/libraries/LibTWAPOracle.sol#L129-L137
https://etherscan.io/address/0x20955CB69Ae1515962177D164dfC9522feef567E#code
Tool used
Manual Review
Recommendation
Use a better price oracle
Duplicate of #56