Two fund-moving functions move tokens out of the vault before they update the vault's own accounting:
withdraw() — the token transfer (contract → user) happens at lib.rs:447, and asset_account.balance -= amount only afterwards at lib.rs:449.
release_payment() — the transfer (contract → orchestrator) happens at lib.rs:723, and task.spent += amount only afterwards at lib.rs:725.
deposit() also transfers before writing state, but that direction (user → contract) is safe. The two above perform an external call while internal state is stale — a textbook checks-effects-interactions (CEI) violation.
Whitelisted assets are admin-controlled today, so this is not currently exploitable. But trust-minimization is the entire purpose of the vault, and the multi-asset roadmap explicitly invites third-party SAC tokens whose transfer could re-enter the contract. Ordering the writes before the transfer removes this whole class of bug and matches standard treasury-contract practice.
Goal
Reorder withdraw and release_payment so that all storage writes and TTL bumps complete before the token::Client::transfer(...) call. The transfer becomes the final interaction; if it panics the whole transaction reverts, so there is no risk of decrementing balance without moving funds.
Requirements & constraints
- Happy-path behavior must be byte-for-byte identical — this is a reordering, not a behavior change.
- Emitted events must still reflect the final post-transfer state (same field values as today).
- Add a short doc-comment on each function stating the CEI ordering so a future edit doesn't silently reintroduce the bug.
Edge cases to consider
release_payment with a partial spend (spent + amount < plan_cost) and with an exact-fill (== plan_cost).
withdraw of exactly the available (balance - locked) amount.
- A transfer that reverts must leave no state change (verify the revert-safety reasoning holds after reordering).
Acceptance criteria
Pointers
withdraw ~ lib.rs:403–468; release_payment ~ lib.rs:687–747.
- The default test token does not re-enter, so this is primarily verified by reading the reordered code; call that out in the PR description.
Notes for contributors
If you'd like to work on this, comment below so we can assign it to you.
Questions welcome — see CONTRIBUTING.md for setup
and workflow.
Two fund-moving functions move tokens out of the vault before they update the vault's own accounting:
withdraw()— the token transfer (contract → user) happens atlib.rs:447, andasset_account.balance -= amountonly afterwards atlib.rs:449.release_payment()— the transfer (contract → orchestrator) happens atlib.rs:723, andtask.spent += amountonly afterwards atlib.rs:725.deposit()also transfers before writing state, but that direction (user → contract) is safe. The two above perform an external call while internal state is stale — a textbook checks-effects-interactions (CEI) violation.Whitelisted assets are admin-controlled today, so this is not currently exploitable. But trust-minimization is the entire purpose of the vault, and the multi-asset roadmap explicitly invites third-party SAC tokens whose
transfercould re-enter the contract. Ordering the writes before the transfer removes this whole class of bug and matches standard treasury-contract practice.Goal
Reorder
withdrawandrelease_paymentso that all storage writes and TTL bumps complete before thetoken::Client::transfer(...)call. The transfer becomes the final interaction; if it panics the whole transaction reverts, so there is no risk of decrementing balance without moving funds.Requirements & constraints
Edge cases to consider
release_paymentwith a partial spend (spent + amount < plan_cost) and with an exact-fill (== plan_cost).withdrawof exactly the available (balance - locked) amount.Acceptance criteria
transferdemonstrates the reordered version is safecargo testandcargo clippy --all-targets -- -D warningspassPointers
withdraw~lib.rs:403–468;release_payment~lib.rs:687–747.Notes for contributors
If you'd like to work on this, comment below so we can assign it to you.
Questions welcome — see CONTRIBUTING.md for setup
and workflow.