@@ -202,6 +202,7 @@ int muldiv_extra(int a, int b, int c) inline {
202202 total_balance -= approximate_amount;
203203 supply -= jetton_amount;
204204 raw_reserve(balance - msg_value - approximate_amount - sent_during_rotation, 0);
205+ available_funds -= approximate_amount;
205206 var msg = begin_cell()
206207 .store_msg_flags(msgflag::NON_BOUNCEABLE)
207208 .store_slice(from_address)
@@ -217,10 +218,17 @@ int muldiv_extra(int a, int b, int c) inline {
217218 }
218219 ;; conservatively use `borrowed` instead of `expected` as amount that will be returned
219220 var [_, _, _, borrowed, _, _, _] = prev_round_borrowers;
220- int funds_at_round_end = available_funds + borrowed - FINALIZE_ROUND_FEE;
221+
222+ ;; max(*, 0) is needed because optimistic withdrawals do not participate in sharing -FINALIZE_ROUND_FEE
223+ ;; When we have both optimistic and pessimistic withdrawals it is ok: pessimists will get both
224+ ;; FINALIZE_ROUND_FEE loss and pool round profit. The only problem is when optimists withdraw 100% value
225+ ;; and there is no profit to cover FINALIZE_ROUND_FEE. In this case available_funds may become negative
226+ ;; and max(*,0) helps with that
227+
228+ int funds_at_round_end = max(available_funds + borrowed - FINALIZE_ROUND_FEE, 0);
221229 (int projected_balance, int projected_supply) = _get_projected_conversion_ratio ();
222- throw_unless( 100,
223- funds_at_round_end > muldiv_extra(requested_for_withdrawal, projected_balance, projected_supply));
230+ throw_unless( 100, ;; error_code doesn't matter it will be catched
231+ funds_at_round_end >= muldiv_extra(requested_for_withdrawal, projected_balance, projected_supply));
224232 } catch (exc_arg, exc_num) {
225233 ;; note that sent_during_rotation is null because
226234 ;; effect of update_round is reverted in catch
0 commit comments