IOP _ ThunderNFT 34542 - [Smart Contract - Insight] Not Handling Balance Entries Properly in the Wit
Last updated
Was this helpful?
Last updated
Was this helpful?
Submitted on Thu Aug 15 2024 11:27:41 GMT-0400 (Atlantic Standard Time) by @bugtester for
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
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.
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.
Add any relevant links to documentation or code
https://github.com/ThunderFuel/smart-contracts/blob/260c9859e2cd28c188e8f6283469bcf57c9347de/contracts-v1/pool/src/main.sw#L105C5-L124C6
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, } }