-
Notifications
You must be signed in to change notification settings - Fork 131
Add unwrap_lamports instruction
#857
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
a49dba9 to
0d937fe
Compare
| } | ||
|
|
||
| async fn wrapped_sol(test_validator: &TestValidator, payer: &Keypair) { | ||
| // both tests use the same ata so they can't run together |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both the original wrapped sol tests and the unwrap lamport tests create and use the payer native mint ata, so one may end up failing depending on the order the tests are run, so i put them here so they run in order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine by me!
| extensions: OptionOrNullable<Array<ExtensionArgs>>; | ||
| }; | ||
|
|
||
| /** Gets the encoder for {@link MintArgs} account data. */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These comments were generated when i ran the generate clients script
| test_context | ||
| } | ||
|
|
||
| async fn make_context_with_native_mint(amount: u64) -> TestContext { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The old tests generate new mints each time so i added this helper for the native mint
| } | ||
| ] | ||
| }, | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually wrote this by hand, is this how it's usually done?
The origin of the file says shank, but i couldn't find any shank macros
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's been written by hand, thanks for handling this!
| } | ||
|
|
||
| let self_transfer = source_account_info.key == destination_account_info.key; | ||
| if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the cpi guard since an unwrap is a transfer
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for thinking about other extensions 🙏
|
@joncinque 🙏 Could I get a high level review of this before I mark it ready for review? |
joncinque
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was in such good shape that I gave it a full review. Thanks for your contribution, and for making it so complete!
Most of the comments are pretty minor, so we should be able to land this pretty quickly.
cc @febo to also give it a look since he wrote the p-token implementation
| signer_pubkeys: &[&Pubkey], | ||
| amount: Option<u64>, | ||
| ) -> Result<Instruction, ProgramError> { | ||
| check_program_account(token_program_id)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since p-token will support this too, may as well relax the check to either token program:
| check_program_account(token_program_id)?; | |
| check_spl_token_program_account(token_program_id)?; |
| /// The amount of lamports to transfer. When an amount is | ||
| /// not specified, the entire balance of the source account will be | ||
| /// transferred. | ||
| #[cfg_attr(feature = "serde", serde(with = "coption_fromstr"))] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is ok, since generally we should do this more for u64 values, but we don't normally serialize amounts as strings.
| } | ||
| ] | ||
| }, | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah it's been written by hand, thanks for handling this!
| // unwrap Some(1) lamports is ok | ||
| token | ||
| .unwrap_lamports( | ||
| &alice_account, | ||
| &bob_account, | ||
| &alice.pubkey(), | ||
| Some(1), | ||
| &[&alice], | ||
| ) | ||
| .await | ||
| .unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we check that the lamport was moved?
| // unwrap None lamports is ok | ||
| token | ||
| .unwrap_lamports( | ||
| &alice_account, | ||
| &bob_account, | ||
| &alice.pubkey(), | ||
| None, | ||
| &[&alice], | ||
| ) | ||
| .await | ||
| .unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we check that all of the lamports were moved and the account was deleted?
| import { PublicKey } from '@solana/web3.js'; | ||
| import { Keypair } from '@solana/web3.js'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: may as well combine these imports
| } | ||
|
|
||
| let self_transfer = source_account_info.key == destination_account_info.key; | ||
| if let Ok(cpi_guard) = source_account.get_extension::<CpiGuard>() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for thinking about other extensions 🙏
| // Revisit this later to see if it's worth adding a check to reduce | ||
| // compute costs, ie: | ||
| // if self_transfer || amount == 0 | ||
| check_program_account(source_account_info.owner)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to check this in every case -- only if self_transfer || amount == 0 works
| .checked_add(amount) | ||
| .ok_or(TokenError::Overflow)?; | ||
|
|
||
| source_account.base.amount = remaining_amount.into(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is also present in the p-token implementation, and now I'm questioning it. We don't decrement the source_account.amount during a self-transfer, but it would definitely be clearer if we did. Meaning, if I unwrap from my wrapped SOL account into itself, I would imagine those lamports go from being part of amount to no longer being part of amount.
What do you think @febo ?
Co-authored-by: Jon C <[email protected]>
Co-authored-by: Jon C <[email protected]>
|
I've made the changes, i'll resolve the conversation with the commit references after the last comment(the one about the p-token implementation) has been resolved since it would affect most of the code. |
Background
This PR adds the
unwrap_lamportsinstruction originally proposed here to the Token-2022 program. It also addresses and closes issue #773.Problem
In order to transfer out lamports from native SOL accounts, currently it is necessary to create and close ATAs or token accounts all the time.
Solution
This PR adds a new
unwrap_lamportsinstruction that allows transferring out lamports directly to any destination account. This eliminates the need for creating temporary native token accounts for the recipient.The new instruction uses the discriminator
45, which is currently unused in Token-2022.The amount to unwrap is specified as an
Option<u64>:None: the entire token balance is unwrapSome(amount): the specified amount is unwrapScope