Skip to content

reclaim buffer account#60

Open
kaze-cow wants to merge 26 commits into
mainfrom
kaze/sc-151-close-buffer-account
Open

reclaim buffer account#60
kaze-cow wants to merge 26 commits into
mainfrom
kaze/sc-151-close-buffer-account

Conversation

@kaze-cow

@kaze-cow kaze-cow commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Description

Adds the ReclaimBuffers instruction. Also add some more capabilities to the test helpers.

Changes

The instruction takes in 3 accounts, state_account, receiver_account, token_account, plus two additional accounts buffer_pda, mint for each buffer to clear. The mint is required to help verify the canonical status of buffer_pda, as well as to call Burn if the account happens to hold excess funds.

The instruction can only be called by the receiver account which is registered in the state_pda

As of right now the state pda has been initialized with no data, so it was changed to allow for initiating a single receiver account (maybe later we rename this to fundsManager or so?). Ultimately it seems we will need to have some sort of account that wields this role so might as well start thinking about it now.

Since a bad actor could send 1 wei/lamport of a token to the buffers just before we close them, the buffer may become unable to be closed and the instruction would fail. This could be used as a griefing tactic, so this instruction will additionally Burn any remaining tokens found in the buffer before closing. Burn is used instead of Transfer because a transfer would likely require conditional allocation and sending--all for what is almost certainly a tiny . So its much easier to simply burn this amount entirely and move on. As this is a potentially destructive operation, multiple warnings have been added around the codebase to prevent this from being overlooked. Additionally, while most tokens support anyone being able to Burn their own tokens, there may exist certain 2022 tokens which disallow it, and so the buffer could not be closed unless we were to empty the account completely.

For the unit tests (which were expanded upon in #53 ), one additional change was made to allow for controlling the signer with is_signer. For right now it arbitrarily assumes any account that doesn't have data is a signer and any account that does have data is not. Tbh I am not sure how to handle this, I think it would be really annoying to have a boolean for every single call to the fake_account* functions. Maybe there is another way?

How to test

  • Confirm is_signer unit testing strategy, could be improved (please provide suggestion!)
  • Confirm ReclaimBuffer general methodology.
  • Consider behavior of burning tokens as a way of reducing the security impact

@kaze-cow kaze-cow self-assigned this Jul 6, 2026
@kaze-cow
kaze-cow requested a review from a team as a code owner July 6, 2026 14:47
@linear-code

linear-code Bot commented Jul 6, 2026

Copy link
Copy Markdown

SC-151

@kaze-cow kaze-cow changed the title Kaze/sc 151 close buffer account reclaim buffer account Jul 7, 2026

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

The PR is fairly large so I'd suggest to split it in 2: first, introducing the new state PDA data and the initialization; then, introducing the new reclaim buffer instruction. If that's large as well you can consider splitting parsing/ix building to the processing but maybe it isn't needed (the burn logic is nontrivial though).


for ReclaimBufferEntry { buffer_pda, mint } in buffers.iter().map(read_buffer_entry) {
let expected_buffer_pda =
Address::find_program_address(&buffer_pda_seeds(mint.address().as_array()), program_id)

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.

You can use find_buffer_pda.

// Sending the tokens to another account is much more complicated because the receiving
// account needs to be loaded and likely initialized with rent--all to handle what is likely
// microdust. So burning is the easiest way to get around this issue.
if amount > 0 {

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 don't like this because there's a race condition here: if we call ReclaimBuffer and at the same time we settle an order that gave us a large fee in this token, then this large fee gets burned.
The expectation in general is that in most cases we won't need to burn tokens.
So my suggestion would be the following:

  • First, we implement this without the burning. If there are still tokens, then we can't close.
  • Next, in another PR, we add some fancy logic to this. What I'm thinking about:
    • There are optional input bytes with an amount. If they aren't specified, we assume it's empty and the transaction is rejected if there are tokens.
    • If they are specified as maxu64, then we burn the full amount. (Maybe non needed if the builtin burning function already does this itself.)
    • If they are specified to any other amount, then exactly that amount is burnt.

This gives us the flexibility to unconditionally burn funds, to play it safe in the standard scenario, and to do something more surgical if needed.

Base automatically changed from kaze/sc-150-close-order-account to main July 17, 2026 09:54
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