IOP _ ThunderNFT 34542 - [Smart Contract - Insight] Not Handling Balance Entries Properly in the Wit
Submitted on Thu Aug 15 2024 11:27:41 GMT-0400 (Atlantic Standard Time) by @bugtester for IOP | ThunderNFT
Report ID: #34542
Report type: Smart Contract
Report severity: Insight
Target: https://github.com/ThunderFuel/smart-contracts/tree/main/contracts-v1/pool
Impacts:
Unbounded gas consumption
Description
Brief/Intro
The current implementation of the withdraw function in our smart contract includes a check and update mechanism for user balances. However, the storage entries for zero balances are retained, leading to unnecessary storage usage and potentially high costs, especially with a large number of users having zero balances
using storage.balance_of.remove(&(sender, asset)), you ensure that entries with a zero balance are not kept in the storage, which optimizes storage usage.
Impact Details
The primary issue with retaining zero balance entries is the unnecessary usage of storage space, which leads to higher costs and potential inefficiencies. This issue becomes more significant when there is a large number of users with zero balances.
References
Add any relevant links to documentation or code
Proof of concept
Proof of Concept
https://github.com/ThunderFuel/smart-contracts/blob/260c9859e2cd28c188e8f6283469bcf57c9347de/contracts-v1/pool/src/main.sw#L105C5-L124C6
fix
updating the withdraw function to remove the storage entry if the new balance after withdrawal is zero. This will optimize storage usage and reduce costs.
#[storage(read, write)] fn withdraw(asset: AssetId, amount: u64) { let sender = msg_sender().unwrap(); let current_balance = _balance_of(sender, asset); require(current_balance >= amount, PoolErrors::AmountHigherThanBalance);
}
/// Returns the balance of the user by the assetId #[storage(read)] fn balance_of(account: Identity, asset: AssetId) -> u64 { _balance_of(account, asset) }
/// Internal function to get the balance of an account for a specific asset #[storage(read)] fn _balance_of(account: Identity, asset: AssetId) -> u64 { let status = storage.balance_of.get(&(account, asset)).try_read(); match status { Option::Some(balance) => *balance, Option::None => 0, } }
Last updated