Skip to content

Validate order limit prices - #75

Merged
fedgiac merged 10 commits into
mainfrom
limit-price-check
Jul 31, 2026
Merged

Validate order limit prices#75
fedgiac merged 10 commits into
mainfrom
limit-price-check

Conversation

@fedgiac

@fedgiac fedgiac commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Make a user intent more meaningful by disallowing the contract to pull funds from the user if the price is worse than what the intent specifies.

Until now the program moved an order's sell tokens (pulls) and paid its buy tokens (pushes) without checking that the amounts respected the order. This adds the per-order checks that back DESIGN.md's user guarantee that the user receives at least the specified buy amount, proportional to how much of the sell amount was used.

This is the first of two PRs: it does the limit price only. The per-order sell/buy caps, fill-or-kill, and cumulative cross-settlement accounting follow separately.

Parsing the push amounts

BeginSettle now also parses the push amounts through introspection, similarly to what was done for the accounts before.
Consistency with parsing is validated by sharing helpers and a proptest.

The check

amount_out * sell_amount >= buy_amount * amount_in

Note that it doesn't matter for this check whether an order is sell or buy.

I run the check after the pulls rather than before to make the code simpler.
I'm a bit concerned for security once token2022 is introduced and CPI on transfer becomes a thing, but for now this code should be safe.

Tests

Added settle_limit_prices.rs with integration tests.
Some of the edge cases are only covered by unit tests, but by the way the code is structured I think it should be enough.

@fedgiac
fedgiac requested a review from a team as a code owner July 17, 2026 20:43
@fedgiac
fedgiac force-pushed the limit-price-check branch from 99a169c to b535496 Compare July 17, 2026 21:09
Base automatically changed from push-funds-to-user-processing-in-finalize-settle to main July 23, 2026 08:10
@fedgiac
fedgiac force-pushed the limit-price-check branch from b535496 to 306f91f Compare July 23, 2026 08:49

@kaze-cow kaze-cow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in the equation you gave amount_out * sell_amount >= buy_amount * amount_in, why not flip it so the terms are matching up order like amount_out * sell_amount >= amount_in * buy_amount? My brain wants to think about it like amounts should be corresponding. This is the case in the code too.

in any case, the equation makes good sense when thinking in terms of a sell price order, you would want to ensure the limit price is respected, i.e.

amount_out >= amount_in * (buy_amount / sell_amount)

the buy_amount / sell_amount becomes an effective trading price for the output and thats scaled against the amount_in. Opposite happens on buy orders naturally. so the maths is good (as if it needed to be questioned!)

I love the new order builder. Now we need a SettleBuilder or so 😆

Comment thread interface/src/instruction/settle/finalize.rs Outdated
Comment thread interface/src/instruction/settle/finalize.rs
Comment thread programs/settlement/tests/settle_limit_prices.rs
Comment thread programs/settlement/tests/settle_limit_prices.rs Outdated
.buy_amount(800_000)
.build();
let violating = OrderBuilder::new(&mut svm, &program_id, &payer)
.kind(OrderKind::Buy)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice on the sell test we use only sells and on the buy test we use only buys. Any reason not to mix and match? otherwise I don't understand why we test to this extent in the first place.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmh, I agree the two tests seem kind of weird. I also noticed that they use different tokens, which make them kind of moot. I changed this so that we mix and match and in general have this test more meaningful, wdyt?
5d045ff

@fedgiac

fedgiac commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

I updated the explanation of the formula to match your comment: be30897
I've seen a few bugs coming from the math not being clear enough, so it's very good to make the explanation as intuitive as possible!

@fedgiac
fedgiac requested a review from kaze-cow July 28, 2026 16:57
@fedgiac fedgiac mentioned this pull request Jul 28, 2026

@kaze-cow kaze-cow left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all comments addressed. just left a couple misc "fyi" comments in the individual commits for the most recent changes.

@fedgiac
fedgiac merged commit 57bcbeb into main Jul 31, 2026
14 checks passed
@fedgiac
fedgiac deleted the limit-price-check branch July 31, 2026 08:02
@fedgiac
fedgiac restored the limit-price-check branch July 31, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants