Skip to content

Add WithdrawNativeSol instruction - #198

Merged
kaze-cow merged 11 commits into
mainfrom
kaze/sc-403-withdraw-sol-from-native-buffer
Oct 12, 2026
Merged

kaze-cow merged 11 commits into
mainfrom
kaze/sc-403-withdraw-sol-from-native-buffer

Conversation

@kaze-cow

@kaze-cow kaze-cow commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Description

Adds a WithdrawNativeSol instruction (discriminator 12) that moves a given amount of lamports out of the native SOL buffer to a recipient of the caller's choosing. Only the settlement-owned-order authority can call it.

Motivation

Fees paid out in native SOL build up in NATIVE_SOL_BUFFER_PDA, and right now nothing can get them out. The other buffers are emptied by selling their contents through CreateSettlementOwnedOrder, but SOL isn't a token, so it can't be the sell side of an order. Because the buffer is owned by the program, we can debit it directly, which makes this a small instruction. See SC-403.

Considerations

Which authority

It's gated by SettlementOwnedOrderAuthority, since that role is already the one trusted with withdrawing fees from the buffers. Adding a new role would have changed the state layout, Initialize args, the IDL and the CLI just to hold a key that would most likely be the same one anyway. The role's doc comment, DESIGN.md and SECURITY_DESIGN.md now say it can do this too.

Protecting the buffer's rent

This uses the same approach as #192: Solana itself rejects a withdrawal that leaves the buffer partly below rent (InsufficientFundsForRent), so the only case the program has to catch is an empty buffer, which it rejects with the existing NativeSolBufferEmptied. I widened that error's doc to cover both instructions instead of adding a near-duplicate. The only new errors are UnauthorizedNativeSolWithdrawal (46) and NativeSolBufferMismatch (47).

Explicit amount vs. sweep

The caller passes an explicit u64 instead of "everything above rent". That way partial withdrawals are possible, and the instruction doesn't have to read the Rent sysvar to work out the sweepable amount.

Cost

withdraw_native_sol/happy_path_withdraws_to_a_recipient_chosen_by_the_authority: 374 CU. The extra dispatch arm moves 15 other rows by 1–3 CU (all down).

Tests

The processor has unit tests for every rejection path and the happy path. Integration tests in tests/withdraw_native_sol.rs cover:

  • withdrawing to a third party
  • withdrawing to the authority itself
  • the other three roles being refused
  • dipping into rent
  • emptying the buffer
  • passing a different program-owned account as the buffer

How to test

just test. For just this instruction: cargo test --test withdraw_native_sol (after just build-program).

🤖 Generated with Claude Code

Lets the settlement-owned-order authority move lamports out of the native
SOL buffer to a recipient of its choosing. The buffer's rent stays put:
the runtime rejects leaving it under-funded, and emptying it is rejected
with the existing NativeSolBufferEmptied.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@kaze-cow
kaze-cow requested a review from a team as a code owner October 9, 2026 13:22
@linear-code

linear-code Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

SC-403

@fedgiac fedgiac 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.

Looks good already. A suggestion to make it easier to withdraw.

Comment thread interface/src/instruction/withdraw_native_sol.rs Outdated
Comment thread interface/src/instruction/withdraw_native_sol.rs Outdated
Comment thread interface/src/instruction/withdraw_native_sol.rs Outdated
Comment thread interface/src/error.rs
Comment thread interface/src/error.rs
Comment on lines +156 to +162
/// `WithdrawNativeSol`'s authority account isn't a signer, or doesn't
/// match the `settlement_owned_order_authority` recorded in the settlement
/// state PDA.
UnauthorizedNativeSolWithdrawal = 47,
/// `WithdrawNativeSol`'s native SOL buffer account isn't the canonical
/// native SOL buffer PDA.
NativeSolBufferMismatch = 48,

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.

Not in this PR, and not before the audit, but we should really clean up all our errors.
This for example should be the same as the settlement-owned order and should have a more generic name. No reason to separate the two. Same for the mismatch, I'm sure there's a generic one.

Comment thread programs/settlement/src/processor/withdraw_native_sol.rs Outdated
Comment thread programs/settlement/tests/idl/generate.rs
Comment thread programs/settlement/tests/withdraw_native_sol.rs Outdated
Comment thread programs/settlement/tests/withdraw_native_sol.rs Outdated
Comment thread programs/settlement/tests/withdraw_native_sol.rs

@fedgiac fedgiac 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.

@kaze-cow please review my changes and approve as well if acceptable.

Implements suggestion from [this
comment](#198 (comment))
to silently cap the amount withdrawn in a `WithdrawNativeSol`
instruction to the full balance minus the rent. This is done so that a
script that automatically withdraws the fees won't occasionally revert
if at the same time a settlement is using funds from the native SOL
buffer.

### How to test

Existing and modified tests.
@kaze-cow

Copy link
Copy Markdown
Contributor Author

I cannot approve my own PR, so I am just going to say:

I approve the latest changes.

@kaze-cow
kaze-cow merged commit a0b5f7d into main Oct 12, 2026
18 checks passed
@kaze-cow
kaze-cow deleted the kaze/sc-403-withdraw-sol-from-native-buffer branch October 12, 2026 08:35
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.

2 participants