feat(treasury): fix multi-token deposit balance accounting gap - #510
Merged
misrasamuelisiguzor-oss merged 2 commits intoAug 29, 2026
Conversation
Closes WHEELBACK#448. Audited contracts/treasury/src/deposits.rs and settlements.rs. Found a genuine gap: DataKey::Balance was keyed only by holder address (Balance(Address)), not by token contract, so deposit/batch_deposit/ withdraw/get_balance silently pooled deposits from every concurrently-allowlisted token into one shared balance per address — depositing 100 USDC then 100 EURC read back as 200, and a withdrawal of either token drew from that mixed total. TokenAllowlist already permits multiple simultaneously-allowed tokens, so this was reachable in normal use, not just a hypothetical. Fixed by re-keying storage as DataKey::Balance(holder, token_contract) in crates/multisig/src/lib.rs, and updating deposit_one/withdraw/get_balance in deposits.rs to read/write the per-(holder, token) bucket. get_balance's public signature now takes an explicit token_contract parameter; updated the existing call sites in treasury_deposit_withdraw_roundtrip_test.rs and reentrancy_suite/{deposit,withdraw}.rs to match, without adding new test coverage. Documented the verified per-token isolation, and the fact that execute_settlement/partially_execute_settlement never touch this ledger (they pay merchants straight out of the treasury's on-chain token balance), in contracts/treasury/README.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NpeEK1u9YTwWabX64LfM43
|
@AMV-AUTOS Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
misrasamuelisiguzor-oss
merged commit Aug 29, 2026
7766fbb
into
WHEELBACK:main
5 of 14 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
contracts/treasury/src/deposits.rsandsettlements.rsas requested. Confirmed a genuine gap, not just a hypothetical one:DataKey::Balancewas keyed only by holder address (Balance(Address)), sodeposit/batch_deposit/withdraw/get_balancepooled deposits from every concurrently-allowlisted token into one shared balance per address — e.g. depositing 100 USDC then 100 EURC read back as a combined 200, and a withdrawal of either token drew from that mixed total.TokenAllowlistalready permits multiple simultaneously-allowed tokens, so this was reachable in normal use.DataKey::Balance(holder, token_contract)incrates/multisig/src/lib.rs, and updatingdeposit_one/withdraw/get_balanceindeposits.rsto read/write the per-(holder, token) bucket.get_balance's public signature now takes an explicittoken_contractparameter.execute_settlement/partially_execute_settlementnever touch this ledger — they pay merchants directly out of the treasury's on-chain token balance viatoken::Client::transfer, so the deposit ledger and the settlement flow are independent accounting paths by design. Documented this and the verified per-token isolation incontracts/treasury/README.md.treasury_deposit_withdraw_roundtrip_test.rsandreentrancy_suite/{deposit,withdraw}.rsto the newget_balancesignature so the crate keeps compiling; no new test coverage was added per the task scope for this batch.Test plan
DataKey::Balanceis only referenced withincontracts/treasury/src/deposits.rs, so the storage-key shape change is containedget_balance/DataKey::Balancecall sites across the repo were updated to the new two-argument shapecargo test --all,cargo clippy -- -D warnings,cargo fmt --all -- --check(not run locally — no Rust toolchain available in this environment; CI will verify)