Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit e832fe3

Browse files
committed
add success_transfer_with_fee test
1 parent 01b945c commit e832fe3

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed

token/program-2022-test/tests/transfer_hook.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use {
1313
entrypoint::ProgramResult,
1414
instruction::{AccountMeta, Instruction, InstructionError},
1515
program_error::ProgramError,
16+
program_option::COption,
1617
pubkey::Pubkey,
1718
signature::Signer,
1819
signer::keypair::Keypair,
@@ -23,6 +24,7 @@ use {
2324
spl_token_2022::{
2425
error::TokenError,
2526
extension::{
27+
transfer_fee::{TransferFee, TransferFeeAmount, TransferFeeConfig},
2628
transfer_hook::{TransferHook, TransferHookAccount},
2729
BaseStateWithExtensions,
2830
},
@@ -36,6 +38,9 @@ use {
3638
std::{convert::TryInto, sync::Arc},
3739
};
3840

41+
const TEST_MAXIMUM_FEE: u64 = 10_000_000;
42+
const TEST_FEE_BASIS_POINTS: u16 = 250;
43+
3944
/// Test program to fail transfer hook, conforms to transfer-hook-interface
4045
pub fn process_instruction_fail(
4146
_program_id: &Pubkey,
@@ -261,6 +266,63 @@ async fn setup(mint: Keypair, program_id: &Pubkey, authority: &Pubkey) -> TestCo
261266
context
262267
}
263268

269+
async fn setup_with_fee(mint: Keypair, program_id: &Pubkey, authority: &Pubkey) -> TestContext {
270+
let mut program_test = setup_program_test(program_id);
271+
272+
let transfer_fee_config_authority = Keypair::new();
273+
let withdraw_withheld_authority = Keypair::new();
274+
let transfer_fee_basis_points = TEST_FEE_BASIS_POINTS;
275+
let maximum_fee = TEST_MAXIMUM_FEE;
276+
add_validation_account(&mut program_test, &mint.pubkey(), program_id);
277+
278+
let context = program_test.start_with_context().await;
279+
let context = Arc::new(tokio::sync::Mutex::new(context));
280+
281+
let mut context = TestContext {
282+
context,
283+
token_context: None,
284+
};
285+
context
286+
.init_token_with_mint_keypair_and_freeze_authority(
287+
mint,
288+
vec![
289+
ExtensionInitializationParams::TransferHook {
290+
authority: Some(*authority),
291+
program_id: Some(*program_id),
292+
},
293+
ExtensionInitializationParams::TransferFeeConfig {
294+
transfer_fee_config_authority: transfer_fee_config_authority.pubkey().into(),
295+
withdraw_withheld_authority: withdraw_withheld_authority.pubkey().into(),
296+
transfer_fee_basis_points,
297+
maximum_fee,
298+
},
299+
],
300+
None,
301+
)
302+
.await
303+
.unwrap();
304+
context
305+
}
306+
307+
fn test_transfer_fee() -> TransferFee {
308+
TransferFee {
309+
epoch: 0.into(),
310+
transfer_fee_basis_points: TEST_FEE_BASIS_POINTS.into(),
311+
maximum_fee: TEST_MAXIMUM_FEE.into(),
312+
}
313+
}
314+
315+
fn test_transfer_fee_config() -> TransferFeeConfig {
316+
let transfer_fee = test_transfer_fee();
317+
TransferFeeConfig {
318+
transfer_fee_config_authority: COption::Some(Pubkey::new_unique()).try_into().unwrap(),
319+
withdraw_withheld_authority: COption::Some(Pubkey::new_unique()).try_into().unwrap(),
320+
withheld_amount: 0.into(),
321+
older_transfer_fee: transfer_fee,
322+
newer_transfer_fee: transfer_fee,
323+
}
324+
}
325+
264326
async fn setup_with_confidential_transfers(
265327
mint: Keypair,
266328
program_id: &Pubkey,
@@ -549,6 +611,88 @@ async fn success_transfer() {
549611
);
550612
}
551613

614+
#[tokio::test]
615+
async fn success_transfer_with_fee() {
616+
let authority = Keypair::new();
617+
let program_id = Pubkey::new_unique();
618+
let mint_keypair = Keypair::new();
619+
620+
let maximum_fee = TEST_MAXIMUM_FEE;
621+
let alice_amount = maximum_fee * 100;
622+
let transfer_amount = maximum_fee;
623+
624+
let token_context = setup_with_fee(mint_keypair, &program_id, &authority.pubkey())
625+
.await
626+
.token_context
627+
.take()
628+
.unwrap();
629+
630+
let (alice_account, bob_account) =
631+
setup_accounts(&token_context, Keypair::new(), Keypair::new(), alice_amount).await;
632+
633+
let transfer_fee_config = test_transfer_fee_config();
634+
let fee = transfer_fee_config
635+
.calculate_epoch_fee(0, transfer_amount)
636+
.unwrap();
637+
638+
token_context
639+
.token
640+
.transfer_with_fee(
641+
&alice_account,
642+
&bob_account,
643+
&token_context.alice.pubkey(),
644+
transfer_amount,
645+
fee,
646+
&[&token_context.alice],
647+
)
648+
.await
649+
.unwrap();
650+
651+
// Get the accounts' state after the transfer
652+
let alice_state = token_context
653+
.token
654+
.get_account_info(&alice_account)
655+
.await
656+
.unwrap();
657+
let bob_state = token_context
658+
.token
659+
.get_account_info(&bob_account)
660+
.await
661+
.unwrap();
662+
663+
// Check that the correct amount was deducted from Alice's account
664+
assert_eq!(alice_state.base.amount, alice_amount - transfer_amount);
665+
666+
// Check the there are no tokens withheld in Alice's account
667+
let extension = alice_state.get_extension::<TransferFeeAmount>().unwrap();
668+
assert_eq!(extension.withheld_amount, 0.into());
669+
670+
// Check the fee tokens are withheld in Bobs's account
671+
let extension = bob_state.get_extension::<TransferFeeAmount>().unwrap();
672+
assert_eq!(extension.withheld_amount, fee.into());
673+
674+
// Check that the correct amount was added to Bobs's account
675+
assert_eq!(bob_state.base.amount, transfer_amount - fee);
676+
677+
// the example program checks that the transferring flag was set to true,
678+
// so make sure that it was correctly unset by the token program
679+
assert_eq!(
680+
bob_state
681+
.get_extension::<TransferHookAccount>()
682+
.unwrap()
683+
.transferring,
684+
false.into()
685+
);
686+
687+
assert_eq!(
688+
alice_state
689+
.get_extension::<TransferHookAccount>()
690+
.unwrap()
691+
.transferring,
692+
false.into()
693+
);
694+
}
695+
552696
#[tokio::test]
553697
async fn fail_transfer_hook_program() {
554698
let authority = Pubkey::new_unique();

0 commit comments

Comments
 (0)