Skip to content

feat(buy-sol) PR 4: accept sponsored orders that buy native SOL - #4990

Open
squadgazzz wants to merge 6 commits into
mainfrom
solana-orderbook/be-332-sponsored-native-sol-buys
Open

squadgazzz wants to merge 6 commits into
mainfrom
solana-orderbook/be-332-sponsored-native-sol-buys

Conversation

@squadgazzz

@squadgazzz squadgazzz commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Description

The orderbook rejects every sponsored order that buys native SOL. #4986 lets the driver settle these orders and #4989 lets them into the auction, so placement can accept them.

A native buy has no buy token account. The payout goes to the owner's wallet and creates it when it's missing, so the mandatory account creation step doesn't apply, and the orderbook rejects a creation step on such an order. FE-674 has to build the creation transaction without it.

Placement also applies the autopilot's native buy rules, so an order that can't settle fails right away instead of waiting out its blockhash in the cut. A buy under 890,880 lamports (the rent-exempt minimum of an empty account), a partially fillable one, or one paying an account the System Program doesn't own gets InvalidNativeBuy. Without a creation step nothing proves the wallet can receive, so placement looks it up once through the sponsoring RPC. A failed lookup answers 500, like the blockhash check next to it. Selling wSOL for native SOL gets SameBuyAndSellToken, like WETH for ETH on EVM, and the quote endpoint answers that pair the same way instead of NoLiquidity. A quote whose native payout comes out under the floor gets InvalidNativeBuy as well, since placement would reject the order it prices.

Merges after #4989, which checks the wallets of pending sponsored native buys.

Changes

  • POST /api/v1/orders accepts native SOL buys, without a buy account creation step
  • Native buys under the rent minimum, partially fillable, or paying an account the System Program doesn't own get InvalidNativeBuy
  • Quotes for a native buy under the rent minimum get InvalidNativeBuy
  • Placement and quotes treat wSOL and native SOL as the same token
  • The OpenAPI spec documents native buys and the new error

How to test

New API and DB tests.

Related issues

BE-332

@linear-code

linear-code Bot commented Sep 28, 2026

Copy link
Copy Markdown

BE-332

@github-actions

Copy link
Copy Markdown

Reminder: Please consider backward compatibility when modifying the API specification.
If breaking changes are unavoidable, ensure:

  • You explicitly pointed out breaking changes.
  • You communicate the changes to affected teams (at least Frontend team and SAFE team).
  • You provide proper versioning and migration mechanisms.

Caused by:

@squadgazzz squadgazzz changed the title solana-orderbook: accept sponsored orders that buy native SOL feat(buy-sol) PR 4: accept sponsored orders that buy native SOL Sep 29, 2026
@squadgazzz
squadgazzz marked this pull request as ready for review September 29, 2026 11:09
@squadgazzz
squadgazzz requested a review from a team as a code owner September 29, 2026 11:09
@claude

claude Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @squadgazzz's task in 3m 43s —— View job


PR Review: accept sponsored orders that buy native SOL

  • Read the PR diff and changed files
  • Understand context (Asset/encode, ENCODED_NATIVE_SOL_TRANSFER, native buy rules)
  • Review for correctness, security, and best practices
  • Post inline comments and summary

Verdict: LGTM — the change is correct, well-scoped, and well-tested. One minor, non-blocking note posted inline.

What I checked

  • Rent floor: Rent::default().minimum_balance(0) = 890,880 lamports, matching the PR. The < comparison correctly accepts exactly 890,880 and rejects 890,879 (test covers this). Sound for both order kinds: a fill-or-kill buy pays exactly buy_amount, a sell pays ≥ buy_amount, so buy_amount ≥ rent_min guarantees the payout clears the floor — and partial fills (which could land under it) are separately rejected. 👍
  • same_token: correctly folds wSOL↔native SOL. For create_order, intent.buy.encode().0 yields the System Program ID sentinel for a native buy (confirmed by the DB test asserting buy_token == [0; 32]), so SameBuyAndSellToken fires when selling wSOL for native SOL.
  • Template check: skipping the mandatory CREATE_DESTINATION step for native buys is right, and the let Asset::TokenProgram(buy) = &intent.buy && … guard means a native buy that does include a destination-creation instruction falls through to InvalidTransaction — covered by create_order_checks_the_preparation_template.
  • Sentinel safety: storing buy_token = 0x00…00 (System Program) as the native marker can't collide with a real SPL mint.
  • OpenAPI: accurately documents native buys, the new InvalidNativeBuy error, and the sentinel encoding.

Note

  • The only thing worth a look is the intentional asymmetry in same_token on the quote path — see the inline comment. Low impact (selling native SOL isn't a supported shape), just a naming/clarity suggestion.

One thing I could not verify from this branch alone: that the rent-minimum and no-partial-fill thresholds here exactly match the autopilot rules from #4989 (not yet merged in). Worth a quick cross-check so an order can't pass placement but get filtered by the auction, or vice-versa.
· solana-orderbook/be-332-sponsored-native-sol-buys

/// Whether an order trades a token for itself. Native SOL counts as wSOL, so
/// selling wSOL for native SOL is a same-token trade.
fn same_token(sell: &Pubkey, buy: &Pubkey) -> bool {
sell == buy || (*sell == native_mint::ID && *buy == ENCODED_NATIVE_SOL_TRANSFER)

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.

Minor / non-blocking: same_token is intentionally asymmetric — it treats sell == wSOL && buy == native as the same token, but not the reverse (sell == native && buy == wSOL). For create_order this is fine since intent.sell is always a real TokenAsset mint. For the quote path, though, a request with sellToken = 11111111111111111111111111111111 (native) and buyToken = wSOL would slip past this check and be forwarded to the quoter instead of returning SameBuyAndSellToken. It'll almost certainly just resolve to NoLiquidity (selling native SOL isn't a supported order shape), so this is low impact — just flagging that the helper's name suggests a symmetric relation that it doesn't provide. If native-SOL is strictly buy-only, a one-line comment saying so would make the asymmetry deliberate.

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.

seems like a good case for covering in unit tests

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.

Added a unit test in 9bfd3fc.

Comment thread crates/solana-orderbook/src/infra/api/routes/create_order/mod.rs Outdated
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