reclaim buffer account#60
Conversation
prevents potential unexpected behavior anywhere else
…cowprotocol/solana-programs into kaze/sc-150-close-order-account
Co-authored-by: Federico Giacon <58218759+fedgiac@users.noreply.github.com>
Co-authored-by: Federico Giacon <58218759+fedgiac@users.noreply.github.com>
Co-authored-by: Federico Giacon <58218759+fedgiac@users.noreply.github.com>
Co-authored-by: Federico Giacon <58218759+fedgiac@users.noreply.github.com>
…checks from pda this requires adding bumps to the input data of the reclaimorder function
fedgiac
left a comment
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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.
Description
Adds the
ReclaimBuffersinstruction. 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 accountsbuffer_pda, mintfor each buffer to clear. Themintis required to help verify the canonical status ofbuffer_pda, as well as to callBurnif the account happens to hold excess funds.The instruction can only be called by the
receiveraccount which is registered in thestate_pdaAs of right now the state pda has been initialized with no data, so it was changed to allow for initiating a single
receiveraccount (maybe later we rename this tofundsManageror 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
Burnany remaining tokens found in the buffer before closing.Burnis used instead ofTransferbecause 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 thefake_account*functions. Maybe there is another way?How to test