sherlock-audit / 2023-05-perennial-judging

12 stars 9 forks source link

bitsurfer - Perennial only use DSU as collateral which pegged to USDC open for depeg failure #227

Closed sherlock-admin closed 1 year ago

sherlock-admin commented 1 year ago

bitsurfer

medium

Perennial only use DSU as collateral which pegged to USDC open for depeg failure

Summary

Perennial only use DSU as collateral which pegged to USDC open for depeg failure

Vulnerability Detail

Perennial utilized DSU as collateral to participate in the protocol. Based on https://docs.perennial.finance/mechanism/dsu, DSU is wrapped version of USDC.

Under the hood, user doesn't need to swap USDC to DSU manually when using Perennial as the protocol wrap that USDC to DSU automatically, if user is using the MultiInvoker contract (or maybe their UI interface).

This wraps assumes that the DSU/USDC price would always be 1:1, which isn’t always the case, for example in the latest USDC depeg, the ratio was changed by a slight difference. In fact, current DSU/USDC rate in Uniswap is 1:0.995.

Looking how past events of current Stable Coins are not guaranteed to be stable, its value should be fetched from an oracle instead, or at least don't just rely on single stable coin.

File: MultiInvoker.sol
237:     function _wrap(address receiver, UFixed18 amount) internal {
238:         // Pull USDC from the `msg.sender`
239:         USDC.pull(msg.sender, amount, true);
240:
241:         _handleWrap(receiver, amount);
242:     }
...
352:     function _handleWrap(address receiver, UFixed18 amount) internal {
353:         // If the batcher is 0 or  doesn't have enough for this wrap, go directly to the reserve
354:         if (address(batcher) == address(0) || amount.gt(DSU.balanceOf(address(batcher)))) {
355:             reserve.mint(amount);
356:             if (receiver != address(this)) DSU.push(receiver, amount);
357:         } else {
358:             // Wrap the USDC into DSU and return to the receiver
359:             batcher.wrap(amount, receiver);
360:         }
361:     }

Impact

DSU is pegged to USDC rather than USD which can cause instability and loss of peg

Code Snippet

https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/multiinvoker/MultiInvoker.sol#L237-L242

See above

Tool used

Manual Review

Recommendation

Instead of pegging the DSU to USDC 1:1, use an oracle to fetch the latest price for example in Uniswap (TWAP), Or maybe implement Oracle with a baskets of stable coin to support, instead of solely depends on single USDC.