Platform: Code4rena
Start Date: 02/02/2024
Pot Size: $100,000 USDC
Total HM: 11
Participants: 27
Period: 28 days
Judge: Lambda
Total Solo HM: 4
Id: 327
League:
Rank: 21/27
Findings: 1
Award: $150.19
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: castle_chain
Also found by: 3docSec, Aymen0909, Franfran, J4X, Ocean_Sky, QiuhaoLi, TheSchnilch, ZanyBonzy, alix40, alkrrrrp, bin2chen, carrotsmuggler, ihtishamsudo, oakcobalt, peachtea, tsvetanovv, zhaojie
150.1907 USDC - $150.19
The protocol is imposing net trading volume limit based on percentage of current liquidity. However this limitation can cause issues during critical moments affecting small volume users. As you can see below, the function set_trade_volume_limit sets trading volume limit per asset.
The critical moments we are talking about here is during the times when users are eagerly trying to convert risky assets into more stable assets like BTC and ETH or stablecoins. When there is current set limit of 50% of an already small liquidity of risky asset pool, this can be easily filled by just one single large trade, preventing other succeeding small users to trade in critical moments, causes loss of funds to them due to quick degrading value of risky asset they want to trade.
If the large users want to intentionally DOS the trading activity of a particular asset, they can easily do it anytime specially during critical period, provided that the volume limit figure can be easily meet by them.
Although the trading volume limit is being reset per block, a large user with resources can still abuse this issue in several blocks, specially to those pools with very small liquidity.
#[pallet::call_index(0)] #[pallet::weight(<T as Config>::WeightInfo::set_trade_volume_limit())] pub fn set_trade_volume_limit( origin: OriginFor<T>, asset_id: T::AssetId, trade_volume_limit: (u32, u32), ) -> DispatchResult { T::TechnicalOrigin::ensure_origin(origin)?; ensure!(asset_id != T::OmnipoolHubAsset::get(), Error::<T>::NotAllowed); Self::validate_limit(trade_volume_limit)?; <TradeVolumeLimitPerAsset<T>>::insert(asset_id, trade_volume_limit); Self::deposit_event(Event::TradeVolumeLimitChanged { asset_id, trade_volume_limit, }); Ok(()) }
pub struct TradeVolumeLimit<T: Config> { pub volume_in: T::Balance, pub volume_out: T::Balance, pub limit: T::Balance, }
Small volume users can't trade in critical moments causing loss of funds to them.
Let's illustrate below a sample scenario wherein a large volume trader just filled in a single transaction the volume limit set by the protocol which is 200,00 units of HDX. Then, the subsequent small volume trader transaction failed in executing his transaction.
Please insert and run this test under file trade_volume.rs in circuit breaker folder.
#[test] fn ensure_and_update_trade_volume_limit_should_fail_when_large_user_filled() { ExtBuilder::default().build().execute_with(|| { //Arrange assert_ok!(CircuitBreaker::initialize_trade_limit(HDX, INITIAL_LIQUIDITY)); assert_ok!(CircuitBreaker::initialize_trade_limit(DOT, INITIAL_LIQUIDITY)); assert_eq!( CircuitBreaker::allowed_trade_volume_limit_per_asset(HDX).unwrap(), TradeVolumeLimit { volume_in: 0, volume_out: 0, limit: 200_000, } ); assert_ok!(CircuitBreaker::ensure_and_update_trade_volume_limit( // large user trade volume in HDX, 200_000, DOT, 0 )); assert_eq!( CircuitBreaker::allowed_trade_volume_limit_per_asset(HDX).unwrap(), // volume status after large user trade TradeVolumeLimit { volume_in: 200__000, volume_out: 0, limit: 200_000, } ); // Act & Assert assert_noop!( CircuitBreaker::ensure_and_update_trade_volume_limit(HDX, 1_000, DOT, 0), // small user trade volume in fail Error::<Test>::TokenInfluxLimitReached ); }); }
Manual Review
Do not allow a single large user to grab the whole volume trade limit in single block transaction. Impose a trading limitation per asset per user account to avoid this abuse on small users.
DoS
#0 - 0xRobocop
2024-03-03T09:38:36Z
Seems like a design decision
#1 - c4-pre-sort
2024-03-03T09:38:43Z
0xRobocop marked the issue as insufficient quality report
#2 - c4-pre-sort
2024-03-03T09:38:46Z
0xRobocop marked the issue as primary issue
#3 - OpenCoreCH
2024-03-08T11:10:38Z
Design decision, that's typically how circuit breakers work. Downgrading to QA
#4 - c4-judge
2024-03-08T11:10:44Z
OpenCoreCH changed the severity to QA (Quality Assurance)
#5 - c4-judge
2024-03-09T10:55:31Z
OpenCoreCH marked the issue as grade-a