#36117 [SC-High] Permanent freezing of tokens when user sends extra tokens as update fee
Submitted on Oct 20th 2024 at 16:42:11 UTC by @savi0ur for IOP | Swaylend
Report ID: #36117
Report Type: Smart Contract
Report severity: High
Target: https://github.com/Swaylend/swaylend-monorepo/blob/9132747331188b86dd8cbf9a1ca37b811d08dddb/contracts/market/src/main.sw
Impacts:
Description
Bug Description
Anyone can update the price of an assets using `update_price_feeds_if_necessary` function.
https://github.com/Swaylend/swaylend-monorepo/blob/9132747331188b86dd8cbf9a1ca37b811d08dddb/contracts/market/src/main.sw#L1269-L1273 ```sway #[payable, storage(read)] fn update_price_feeds_if_necessary(price_data_update: PriceDataUpdate) { reentrancy_guard(); update_price_feeds_if_necessary_internal(price_data_update) } ```
https://github.com/Swaylend/swaylend-monorepo/blob/9132747331188b86dd8cbf9a1ca37b811d08dddb/contracts/market/src/main.sw#L1441-L1469 ```sway #[payable, storage(read)] fn update_price_feeds_if_necessary_internal(price_data_update: PriceDataUpdate) { let contract_id = storage.pyth_contract_id.read(); require( contract_id != ContractId::zero(), Error::OracleContractIdNotSet, );
} ```
As we can see, in `update_price_feeds_if_necessary_internal` function, its accepting `msg_amount() >= update_fee`, if `msg_amount` is strictly greater than `update_fee`, their delta i.e., `msg_amount() - update_fee` will remain in the market contract instead of returning it back to caller i.e., `msg_sender()`. Due to this, caller lose those extra tokens and it gets stuck permanently in the contract.
Impact
Permanent freezing of tokens when user sends extra tokens as update fee while updating price using `update_price_feeds_if_necessary` function.
Recommendation
Either return the remaining tokens back to the caller at the end of function OR make sure `update_price_feeds_if_necessary_internal` function accepts `msg_amount()` exactly equal to `update_fee`.
References
https://github.com/Swaylend/swaylend-monorepo/blob/9132747331188b86dd8cbf9a1ca37b811d08dddb/contracts/market/src/main.sw#L1269-L1273
https://github.com/Swaylend/swaylend-monorepo/blob/9132747331188b86dd8cbf9a1ca37b811d08dddb/contracts/market/src/main.sw#L1441-L1469
Proof Of Concept
Steps to Run:
Open terminal and run `cd swaylend-monorepo`
Paste following rust code in `contracts/market/tests/local_tests/scenarios/price_changes.rs`
Run test using `cargo test --package market --test integration_tests -- local_tests::scenarios::price_changes::excess_fee_not_returned --exact --show-output`
```rust #[tokio::test] async fn excess_fee_not_returned() { let TestData { alice, market, assets, eth, oracle, price_feed_ids, publish_time, prices, .. } = setup(None).await;
} ```
Console Output:
```shell ---- local_tests::scenarios::price_changes::excess_fee_not_returned stdout ---- Price for UNI = 5 Price for BTC = 70000 Price for ETH = 3500 Price for USDC = 1 Alice Balance Before: 10000000000 Alice is sending `extra fee (20) + update_fee (100) = 120` to update_price_feeds_if_necessary Alice Balance After: 9999999879 Difference in balance: 121 NOTE: Its using complete `extra fee (20) + update_fee (100) = 120`, even though update fee is just 100
successes: local_tests::scenarios::price_changes::excess_fee_not_returned ```