Skip to content

Commit a067c24

Browse files
author
Edward (Mike's bot)
committed
fix(order-book): zero unsettled_* before token transfers in settle_funds (CEI)
settle_funds was setting market_user.unsettled_base = 0 and unsettled_quote = 0 *after* the transfer_checked CPIs. Solana CPIs are not reentrant in the EVM sense, but checks-effects-interactions is still the right discipline: if either transfer ever gained a path that called back into this program (custom token hooks, transfer-fee extension with a side effect, a future ext we don't anticipate), having the unsettled_* counters still readable mid-transfer would let a re-entry double-withdraw the balance. Snapshot the amounts into locals, zero the counters, then issue the transfers. The instruction stays atomic via Solana's tx semantics \u2014 if a transfer fails the whole tx unwinds, counters and all. No semantic change in the success path. Tests still pass 24/24.
1 parent 8f73fa3 commit a067c24

1 file changed

Lines changed: 9 additions & 2 deletions

File tree

defi/order-book/anchor/programs/order-book/src/instructions/settle_funds.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,17 @@ pub fn handle_settle_funds(context: Context<SettleFunds>) -> Result<()> {
1010
let market_user = &mut context.accounts.market_user;
1111
let market = &context.accounts.market;
1212

13+
// Snapshot the amounts the user is owed, then zero the counters
14+
// BEFORE the token transfers. Checks-effects-interactions: even though
15+
// Solana CPIs don't reenter in the EVM sense, if either transfer ever
16+
// gained a path that called back into this program (custom token
17+
// hooks, transfer-fee extensions with side effects, ...), having stale
18+
// unsettled_* values readable mid-transfer would let a re-entry double-
19+
// withdraw. Updating state first makes that class of bug impossible.
1320
let base_amount = market_user.unsettled_base;
1421
let quote_amount = market_user.unsettled_quote;
22+
market_user.unsettled_base = 0;
23+
market_user.unsettled_quote = 0;
1524

1625
// Seeds to sign as the market PDA (the authority of both vaults). Built
1726
// once and reused for the two possible transfers.
@@ -39,7 +48,6 @@ pub fn handle_settle_funds(context: Context<SettleFunds>) -> Result<()> {
3948
base_amount,
4049
context.accounts.base_mint.decimals,
4150
)?;
42-
market_user.unsettled_base = 0;
4351
}
4452

4553
if quote_amount > 0 {
@@ -57,7 +65,6 @@ pub fn handle_settle_funds(context: Context<SettleFunds>) -> Result<()> {
5765
quote_amount,
5866
context.accounts.quote_mint.decimals,
5967
)?;
60-
market_user.unsettled_quote = 0;
6168
}
6269

6370
Ok(())

0 commit comments

Comments
 (0)