15
15
} ,
16
16
instruction,
17
17
} ,
18
- spl_token_client:: token:: { ExtensionInitializationParams , TokenError as TokenClientError } ,
18
+ spl_token_client:: {
19
+ client:: ProgramBanksClientProcessTransaction ,
20
+ token:: { ExtensionInitializationParams , Token , TokenError as TokenClientError } ,
21
+ } ,
19
22
std:: convert:: TryInto ,
20
23
} ;
21
24
@@ -302,7 +305,7 @@ async fn set_fee() {
302
305
}
303
306
304
307
#[ tokio:: test]
305
- async fn fail_set_fee_unsupported_mint ( ) {
308
+ async fn fail_unsupported_mint ( ) {
306
309
let mut context = TestContext :: new ( ) . await ;
307
310
context. init_token_with_mint ( vec ! [ ] ) . await . unwrap ( ) ;
308
311
let TokenContext {
@@ -323,6 +326,17 @@ async fn fail_set_fee_unsupported_mint() {
323
326
TransactionError :: InstructionError ( 0 , InstructionError :: InvalidAccountData )
324
327
) ) )
325
328
) ;
329
+ let error = token
330
+ . harvest_withheld_tokens_to_mint ( & [ ] )
331
+ . await
332
+ . err ( )
333
+ . unwrap ( ) ;
334
+ assert_eq ! (
335
+ error,
336
+ TokenClientError :: Client ( Box :: new( TransportError :: TransactionError (
337
+ TransactionError :: InstructionError ( 0 , InstructionError :: InvalidAccountData )
338
+ ) ) )
339
+ ) ;
326
340
}
327
341
328
342
#[ tokio:: test]
@@ -993,3 +1007,216 @@ async fn no_fees_from_self_transfer() {
993
1007
let extension = alice_state. get_extension :: < TransferFeeAmount > ( ) . unwrap ( ) ;
994
1008
assert_eq ! ( extension. withheld_amount, 0 . into( ) ) ;
995
1009
}
1010
+
1011
+ async fn create_and_transfer_to_account (
1012
+ token : & Token < ProgramBanksClientProcessTransaction , Keypair > ,
1013
+ source : & Pubkey ,
1014
+ authority : & Keypair ,
1015
+ owner : & Pubkey ,
1016
+ amount : u64 ,
1017
+ decimals : u8 ,
1018
+ ) -> Pubkey {
1019
+ let account = token
1020
+ . create_auxiliary_token_account ( & Keypair :: new ( ) , owner)
1021
+ . await
1022
+ . unwrap ( ) ;
1023
+ token
1024
+ . transfer_checked ( source, & account, authority, amount, decimals)
1025
+ . await
1026
+ . unwrap ( ) ;
1027
+ account
1028
+ }
1029
+
1030
+ #[ tokio:: test]
1031
+ async fn harvest_withheld_tokens_to_mint ( ) {
1032
+ let TransferFeeConfigWithKeypairs {
1033
+ transfer_fee_config_authority,
1034
+ withdraw_withheld_authority,
1035
+ transfer_fee_config,
1036
+ ..
1037
+ } = test_transfer_fee_config_with_keypairs ( ) ;
1038
+ let mut context = TestContext :: new ( ) . await ;
1039
+ let transfer_fee_basis_points = u16:: from (
1040
+ transfer_fee_config
1041
+ . newer_transfer_fee
1042
+ . transfer_fee_basis_points ,
1043
+ ) ;
1044
+ let maximum_fee = u64:: from ( transfer_fee_config. newer_transfer_fee . maximum_fee ) ;
1045
+ context
1046
+ . init_token_with_mint ( vec ! [ ExtensionInitializationParams :: TransferFeeConfig {
1047
+ transfer_fee_config_authority: transfer_fee_config_authority. pubkey( ) . into( ) ,
1048
+ withdraw_withheld_authority: withdraw_withheld_authority. pubkey( ) . into( ) ,
1049
+ transfer_fee_basis_points,
1050
+ maximum_fee,
1051
+ } ] )
1052
+ . await
1053
+ . unwrap ( ) ;
1054
+ let TokenContext {
1055
+ decimals,
1056
+ mint_authority,
1057
+ token,
1058
+ alice,
1059
+ ..
1060
+ } = context. token_context . as_ref ( ) . unwrap ( ) ;
1061
+
1062
+ let alice_account = Keypair :: new ( ) ;
1063
+ let alice_account = token
1064
+ . create_auxiliary_token_account ( & alice_account, & alice. pubkey ( ) )
1065
+ . await
1066
+ . unwrap ( ) ;
1067
+
1068
+ // mint a lot of tokens
1069
+ let amount = maximum_fee;
1070
+ let alice_amount = amount * 100 ;
1071
+ token
1072
+ . mint_to ( & alice_account, mint_authority, alice_amount)
1073
+ . await
1074
+ . unwrap ( ) ;
1075
+
1076
+ // harvest from zero accounts
1077
+ token. harvest_withheld_tokens_to_mint ( & [ ] ) . await . unwrap ( ) ;
1078
+ let state = token. get_mint_info ( ) . await . unwrap ( ) ;
1079
+ let extension = state. get_extension :: < TransferFeeConfig > ( ) . unwrap ( ) ;
1080
+ assert_eq ! ( extension. withheld_amount, 0 . into( ) ) ;
1081
+
1082
+ // harvest from one account
1083
+ let accumulated_fees = transfer_fee_config. calculate_epoch_fee ( 0 , amount) . unwrap ( ) ;
1084
+ let account = create_and_transfer_to_account (
1085
+ token,
1086
+ & alice_account,
1087
+ alice,
1088
+ & alice. pubkey ( ) ,
1089
+ amount,
1090
+ * decimals,
1091
+ )
1092
+ . await ;
1093
+ token
1094
+ . harvest_withheld_tokens_to_mint ( & [ & account] )
1095
+ . await
1096
+ . unwrap ( ) ;
1097
+ let state = token. get_account_info ( & account) . await . unwrap ( ) ;
1098
+ let extension = state. get_extension :: < TransferFeeAmount > ( ) . unwrap ( ) ;
1099
+ assert_eq ! ( extension. withheld_amount, 0 . into( ) ) ;
1100
+ let state = token. get_mint_info ( ) . await . unwrap ( ) ;
1101
+ let extension = state. get_extension :: < TransferFeeConfig > ( ) . unwrap ( ) ;
1102
+ assert_eq ! ( extension. withheld_amount, accumulated_fees. into( ) ) ;
1103
+
1104
+ // harvest again from the same account
1105
+ token
1106
+ . harvest_withheld_tokens_to_mint ( & [ & account] )
1107
+ . await
1108
+ . unwrap ( ) ;
1109
+ let state = token. get_account_info ( & account) . await . unwrap ( ) ;
1110
+ let extension = state. get_extension :: < TransferFeeAmount > ( ) . unwrap ( ) ;
1111
+ assert_eq ! ( extension. withheld_amount, 0 . into( ) ) ;
1112
+ let state = token. get_mint_info ( ) . await . unwrap ( ) ;
1113
+ let extension = state. get_extension :: < TransferFeeConfig > ( ) . unwrap ( ) ;
1114
+ assert_eq ! ( extension. withheld_amount, accumulated_fees. into( ) ) ;
1115
+
1116
+ // no fail harvesting from account belonging to different mint, but nothing
1117
+ // happens
1118
+ let account = create_and_transfer_to_account (
1119
+ token,
1120
+ & alice_account,
1121
+ alice,
1122
+ & alice. pubkey ( ) ,
1123
+ amount,
1124
+ * decimals,
1125
+ )
1126
+ . await ;
1127
+ context
1128
+ . init_token_with_mint ( vec ! [ ExtensionInitializationParams :: TransferFeeConfig {
1129
+ transfer_fee_config_authority: transfer_fee_config_authority. pubkey( ) . into( ) ,
1130
+ withdraw_withheld_authority: withdraw_withheld_authority. pubkey( ) . into( ) ,
1131
+ transfer_fee_basis_points,
1132
+ maximum_fee,
1133
+ } ] )
1134
+ . await
1135
+ . unwrap ( ) ;
1136
+ let TokenContext { token, .. } = context. token_context . unwrap ( ) ;
1137
+ token
1138
+ . harvest_withheld_tokens_to_mint ( & [ & account] )
1139
+ . await
1140
+ . unwrap ( ) ;
1141
+ let state = token. get_mint_info ( ) . await . unwrap ( ) ;
1142
+ let extension = state. get_extension :: < TransferFeeConfig > ( ) . unwrap ( ) ;
1143
+ assert_eq ! ( extension. withheld_amount, 0 . into( ) ) ;
1144
+ }
1145
+
1146
+ #[ tokio:: test]
1147
+ async fn max_harvest_withheld_tokens_to_mint ( ) {
1148
+ let TransferFeeConfigWithKeypairs {
1149
+ transfer_fee_config_authority,
1150
+ withdraw_withheld_authority,
1151
+ transfer_fee_config,
1152
+ ..
1153
+ } = test_transfer_fee_config_with_keypairs ( ) ;
1154
+ let mut context = TestContext :: new ( ) . await ;
1155
+ let transfer_fee_basis_points = u16:: from (
1156
+ transfer_fee_config
1157
+ . newer_transfer_fee
1158
+ . transfer_fee_basis_points ,
1159
+ ) ;
1160
+ let maximum_fee = u64:: from ( transfer_fee_config. newer_transfer_fee . maximum_fee ) ;
1161
+ context
1162
+ . init_token_with_mint ( vec ! [ ExtensionInitializationParams :: TransferFeeConfig {
1163
+ transfer_fee_config_authority: transfer_fee_config_authority. pubkey( ) . into( ) ,
1164
+ withdraw_withheld_authority: withdraw_withheld_authority. pubkey( ) . into( ) ,
1165
+ transfer_fee_basis_points,
1166
+ maximum_fee,
1167
+ } ] )
1168
+ . await
1169
+ . unwrap ( ) ;
1170
+ let TokenContext {
1171
+ decimals,
1172
+ mint_authority,
1173
+ token,
1174
+ alice,
1175
+ ..
1176
+ } = context. token_context . unwrap ( ) ;
1177
+
1178
+ let alice_account = Keypair :: new ( ) ;
1179
+ let alice_account = token
1180
+ . create_auxiliary_token_account ( & alice_account, & alice. pubkey ( ) )
1181
+ . await
1182
+ . unwrap ( ) ;
1183
+
1184
+ // mint a lot of tokens
1185
+ let amount = maximum_fee;
1186
+ let alice_amount = amount * 100 ;
1187
+ token
1188
+ . mint_to ( & alice_account, & mint_authority, alice_amount)
1189
+ . await
1190
+ . unwrap ( ) ;
1191
+ // harvest from max accounts, which is around 35, AKA 34 accounts + 1 mint
1192
+ // see https://docs.solana.com/proposals/transactions-v2#problem
1193
+ let mut accounts = vec ! [ ] ;
1194
+ let max_accounts = 34 ;
1195
+ for _ in 0 ..max_accounts {
1196
+ let account = create_and_transfer_to_account (
1197
+ & token,
1198
+ & alice_account,
1199
+ & alice,
1200
+ & alice. pubkey ( ) ,
1201
+ amount,
1202
+ decimals,
1203
+ )
1204
+ . await ;
1205
+ accounts. push ( account) ;
1206
+ }
1207
+ let accounts: Vec < _ > = accounts. iter ( ) . collect ( ) ;
1208
+ let accumulated_fees =
1209
+ max_accounts * transfer_fee_config. calculate_epoch_fee ( 0 , amount) . unwrap ( ) ;
1210
+ token
1211
+ . harvest_withheld_tokens_to_mint ( & accounts)
1212
+ . await
1213
+ . unwrap ( ) ;
1214
+ for account in accounts {
1215
+ let state = token. get_account_info ( account) . await . unwrap ( ) ;
1216
+ let extension = state. get_extension :: < TransferFeeAmount > ( ) . unwrap ( ) ;
1217
+ assert_eq ! ( extension. withheld_amount, 0 . into( ) ) ;
1218
+ }
1219
+ let state = token. get_mint_info ( ) . await . unwrap ( ) ;
1220
+ let extension = state. get_extension :: < TransferFeeConfig > ( ) . unwrap ( ) ;
1221
+ assert_eq ! ( extension. withheld_amount, accumulated_fees. into( ) ) ;
1222
+ }
0 commit comments