|
5 | 5 | program_test::{TestContext, TokenContext},
|
6 | 6 | solana_program_test::tokio,
|
7 | 7 | solana_sdk::{
|
8 |
| - instruction::InstructionError, program_option::COption, pubkey::Pubkey, signature::Signer, |
9 |
| - signer::keypair::Keypair, transaction::TransactionError, transport::TransportError, |
| 8 | + instruction::InstructionError, |
| 9 | + program_option::COption, |
| 10 | + pubkey::Pubkey, |
| 11 | + signature::Signer, |
| 12 | + signer::keypair::Keypair, |
| 13 | + system_instruction, |
| 14 | + transaction::{Transaction, TransactionError}, |
| 15 | + transport::TransportError, |
10 | 16 | },
|
11 | 17 | spl_token_2022::{error::TokenError, extension::ExtensionType, state::Account},
|
12 | 18 | spl_token_client::token::{ExtensionInitializationParams, TokenError as TokenClientError},
|
13 | 19 | std::convert::TryInto,
|
| 20 | + test_case::test_case, |
14 | 21 | };
|
15 | 22 |
|
16 | 23 | #[tokio::test]
|
@@ -188,3 +195,90 @@ async fn reallocate_without_current_extension_knowledge() {
|
188 | 195 | ])
|
189 | 196 | );
|
190 | 197 | }
|
| 198 | + |
| 199 | +#[test_case(&[ExtensionType::CpiGuard], 1_000_000_000, true ; "transfer more than new rent and sync")] |
| 200 | +#[test_case(&[ExtensionType::CpiGuard], 1_000_000_000, false ; "transfer more than new rent")] |
| 201 | +#[test_case(&[ExtensionType::CpiGuard], 1, true ; "transfer less than new rent and sync")] |
| 202 | +#[test_case(&[ExtensionType::CpiGuard], 1, false ; "transfer less than new rent")] |
| 203 | +#[test_case(&[ExtensionType::CpiGuard], 0, false ; "no transfer with extension")] |
| 204 | +#[test_case(&[], 1_000_000_000, true ; "transfer lamports and sync without extension")] |
| 205 | +#[test_case(&[], 1_000_000_000, false ; "transfer lamports without extension")] |
| 206 | +#[test_case(&[], 0, false ; "no transfer without extension")] |
| 207 | +#[tokio::test] |
| 208 | +async fn reallocate_updates_native_rent_exemption( |
| 209 | + extensions: &[ExtensionType], |
| 210 | + transfer_lamports: u64, |
| 211 | + sync_native: bool, |
| 212 | +) { |
| 213 | + let mut context = TestContext::new().await; |
| 214 | + context.init_token_with_native_mint().await.unwrap(); |
| 215 | + let TokenContext { token, alice, .. } = context.token_context.unwrap(); |
| 216 | + let context = context.context.clone(); |
| 217 | + |
| 218 | + let alice_account = Keypair::new(); |
| 219 | + token |
| 220 | + .create_auxiliary_token_account(&alice_account, &alice.pubkey()) |
| 221 | + .await |
| 222 | + .unwrap(); |
| 223 | + let alice_account = alice_account.pubkey(); |
| 224 | + |
| 225 | + // transfer more lamports |
| 226 | + if transfer_lamports > 0 { |
| 227 | + let mut context = context.lock().await; |
| 228 | + let instructions = vec![system_instruction::transfer( |
| 229 | + &context.payer.pubkey(), |
| 230 | + &alice_account, |
| 231 | + transfer_lamports, |
| 232 | + )]; |
| 233 | + let tx = Transaction::new_signed_with_payer( |
| 234 | + &instructions, |
| 235 | + Some(&context.payer.pubkey()), |
| 236 | + &[&context.payer], |
| 237 | + context.last_blockhash, |
| 238 | + ); |
| 239 | + context.banks_client.process_transaction(tx).await.unwrap(); |
| 240 | + } |
| 241 | + |
| 242 | + // amount in the account should be 0 no matter what |
| 243 | + let account_info = token.get_account_info(&alice_account).await.unwrap(); |
| 244 | + assert_eq!(account_info.base.amount, 0); |
| 245 | + |
| 246 | + if sync_native { |
| 247 | + token.sync_native(&alice_account).await.unwrap(); |
| 248 | + let account_info = token.get_account_info(&alice_account).await.unwrap(); |
| 249 | + assert_eq!(account_info.base.amount, transfer_lamports); |
| 250 | + } |
| 251 | + |
| 252 | + let token_account = token.get_account_info(&alice_account).await.unwrap(); |
| 253 | + let pre_amount = token_account.base.amount; |
| 254 | + let pre_rent_exempt_reserve = token_account.base.is_native.unwrap(); |
| 255 | + |
| 256 | + // reallocate resizes account to accommodate new extension |
| 257 | + token |
| 258 | + .reallocate(&alice_account, &alice.pubkey(), extensions, &[&alice]) |
| 259 | + .await |
| 260 | + .unwrap(); |
| 261 | + |
| 262 | + let account = token.get_account(alice_account).await.unwrap(); |
| 263 | + assert_eq!( |
| 264 | + account.data.len(), |
| 265 | + ExtensionType::get_account_len::<Account>(extensions) |
| 266 | + ); |
| 267 | + let expected_rent_exempt_reserve = { |
| 268 | + let mut context = context.lock().await; |
| 269 | + let rent = context.banks_client.get_rent().await.unwrap(); |
| 270 | + rent.minimum_balance(account.data.len()) |
| 271 | + }; |
| 272 | + let token_account = token.get_account_info(&alice_account).await.unwrap(); |
| 273 | + let post_amount = token_account.base.amount; |
| 274 | + let post_rent_exempt_reserve = token_account.base.is_native.unwrap(); |
| 275 | + // amount of lamports should be totally unchanged |
| 276 | + assert_eq!(pre_amount, post_amount); |
| 277 | + // but rent exempt reserve should change |
| 278 | + assert_eq!(post_rent_exempt_reserve, expected_rent_exempt_reserve); |
| 279 | + if extensions.is_empty() { |
| 280 | + assert_eq!(pre_rent_exempt_reserve, post_rent_exempt_reserve); |
| 281 | + } else { |
| 282 | + assert!(pre_rent_exempt_reserve < post_rent_exempt_reserve); |
| 283 | + } |
| 284 | +} |
0 commit comments