7
7
instruction:: TransferFeeInstruction , TransferFee , TransferFeeAmount ,
8
8
TransferFeeConfig , MAX_FEE_BASIS_POINTS ,
9
9
} ,
10
- StateWithExtensionsMut ,
10
+ StateWithExtensions , StateWithExtensionsMut ,
11
11
} ,
12
12
processor:: Processor ,
13
13
state:: { Account , Mint } ,
@@ -154,9 +154,8 @@ fn process_withdraw_withheld_tokens_from_mint(
154
154
155
155
fn harvest_from_account < ' a , ' b > (
156
156
mint_key : & ' b Pubkey ,
157
- mint_extension : & ' b mut TransferFeeConfig ,
158
157
token_account_info : & ' b AccountInfo < ' a > ,
159
- ) -> Result < ( ) , TokenError > {
158
+ ) -> Result < u64 , TokenError > {
160
159
let mut token_account_data = token_account_info. data . borrow_mut ( ) ;
161
160
let mut token_account = StateWithExtensionsMut :: < Account > :: unpack ( & mut token_account_data)
162
161
. map_err ( |_| TokenError :: InvalidState ) ?;
@@ -168,13 +167,8 @@ fn harvest_from_account<'a, 'b>(
168
167
. get_extension_mut :: < TransferFeeAmount > ( )
169
168
. map_err ( |_| TokenError :: InvalidState ) ?;
170
169
let account_withheld_amount = u64:: from ( token_account_extension. withheld_amount ) ;
171
- let mint_withheld_amount = u64:: from ( mint_extension. withheld_amount ) ;
172
- mint_extension. withheld_amount = mint_withheld_amount
173
- . checked_add ( account_withheld_amount)
174
- . ok_or ( TokenError :: Overflow ) ?
175
- . into ( ) ;
176
170
token_account_extension. withheld_amount = 0 . into ( ) ;
177
- Ok ( ( ) )
171
+ Ok ( account_withheld_amount )
178
172
}
179
173
180
174
fn process_harvest_withheld_tokens_to_mint ( accounts : & [ AccountInfo ] ) -> ProgramResult {
@@ -187,18 +181,92 @@ fn process_harvest_withheld_tokens_to_mint(accounts: &[AccountInfo]) -> ProgramR
187
181
let mint_extension = mint. get_extension_mut :: < TransferFeeConfig > ( ) ?;
188
182
189
183
for token_account_info in token_account_infos {
190
- match harvest_from_account ( mint_account_info. key , mint_extension, token_account_info) {
191
- // Shouldn't ever happen, but if it does, we don't want to propagate any half-done changes
192
- Err ( TokenError :: Overflow ) => return Err ( TokenError :: Overflow . into ( ) ) ,
184
+ match harvest_from_account ( mint_account_info. key , token_account_info) {
185
+ Ok ( amount) => {
186
+ let mint_withheld_amount = u64:: from ( mint_extension. withheld_amount ) ;
187
+ mint_extension. withheld_amount = mint_withheld_amount
188
+ . checked_add ( amount)
189
+ . ok_or ( TokenError :: Overflow ) ?
190
+ . into ( ) ;
191
+ }
193
192
Err ( e) => {
194
193
msg ! ( "Error harvesting from {}: {}" , token_account_info. key, e) ;
195
194
}
196
- Ok ( _) => { }
197
195
}
198
196
}
199
197
Ok ( ( ) )
200
198
}
201
199
200
+ fn process_withdraw_withheld_tokens_from_accounts (
201
+ program_id : & Pubkey ,
202
+ accounts : & [ AccountInfo ] ,
203
+ num_token_accounts : u8 ,
204
+ ) -> ProgramResult {
205
+ let account_info_iter = & mut accounts. iter ( ) ;
206
+ let mint_account_info = next_account_info ( account_info_iter) ?;
207
+ let dest_account_info = next_account_info ( account_info_iter) ?;
208
+ let authority_info = next_account_info ( account_info_iter) ?;
209
+ let authority_info_data_len = authority_info. data_len ( ) ;
210
+ let account_infos = account_info_iter. as_slice ( ) ;
211
+ let num_signers = account_infos
212
+ . len ( )
213
+ . saturating_sub ( num_token_accounts as usize ) ;
214
+
215
+ let mint_data = mint_account_info. data . borrow ( ) ;
216
+ let mint = StateWithExtensions :: < Mint > :: unpack ( & mint_data) ?;
217
+ let extension = mint. get_extension :: < TransferFeeConfig > ( ) ?;
218
+
219
+ let withdraw_withheld_authority = Option :: < Pubkey > :: from ( extension. withdraw_withheld_authority )
220
+ . ok_or ( TokenError :: NoAuthorityExists ) ?;
221
+ Processor :: validate_owner (
222
+ program_id,
223
+ & withdraw_withheld_authority,
224
+ authority_info,
225
+ authority_info_data_len,
226
+ & account_infos[ ..num_signers] ,
227
+ ) ?;
228
+
229
+ let mut dest_account_data = dest_account_info. data . borrow_mut ( ) ;
230
+ let mut dest_account = StateWithExtensionsMut :: < Account > :: unpack ( & mut dest_account_data) ?;
231
+ if dest_account. base . mint != * mint_account_info. key {
232
+ return Err ( TokenError :: MintMismatch . into ( ) ) ;
233
+ }
234
+ if dest_account. base . is_frozen ( ) {
235
+ return Err ( TokenError :: AccountFrozen . into ( ) ) ;
236
+ }
237
+ for account_info in & account_infos[ num_signers..] {
238
+ // self-harvest, can't double-borrow the underlying data
239
+ if account_info. key == dest_account_info. key {
240
+ let token_account_extension = dest_account
241
+ . get_extension_mut :: < TransferFeeAmount > ( )
242
+ . map_err ( |_| TokenError :: InvalidState ) ?;
243
+ let account_withheld_amount = u64:: from ( token_account_extension. withheld_amount ) ;
244
+ token_account_extension. withheld_amount = 0 . into ( ) ;
245
+ dest_account. base . amount = dest_account
246
+ . base
247
+ . amount
248
+ . checked_add ( account_withheld_amount)
249
+ . ok_or ( TokenError :: Overflow ) ?;
250
+ } else {
251
+ match harvest_from_account ( mint_account_info. key , account_info) {
252
+ Ok ( amount) => {
253
+ dest_account. base . amount = dest_account
254
+ . base
255
+ . amount
256
+ . checked_add ( amount)
257
+ . ok_or ( TokenError :: Overflow ) ?;
258
+ }
259
+ Err ( e) => {
260
+ msg ! ( "Error harvesting from {}: {}" , account_info. key, e) ;
261
+ }
262
+ }
263
+ }
264
+ }
265
+ dest_account. pack_base ( ) ;
266
+
267
+ Ok ( ( ) )
268
+ }
269
+
202
270
pub ( crate ) fn process_instruction (
203
271
program_id : & Pubkey ,
204
272
accounts : & [ AccountInfo ] ,
@@ -231,8 +299,9 @@ pub(crate) fn process_instruction(
231
299
msg ! ( "TransferFeeInstruction: WithdrawWithheldTokensFromMint" ) ;
232
300
process_withdraw_withheld_tokens_from_mint ( program_id, accounts)
233
301
}
234
- TransferFeeInstruction :: WithdrawWithheldTokensFromAccounts => {
235
- unimplemented ! ( ) ;
302
+ TransferFeeInstruction :: WithdrawWithheldTokensFromAccounts { num_token_accounts } => {
303
+ msg ! ( "TransferFeeInstruction: WithdrawWithheldTokensFromAccounts" ) ;
304
+ process_withdraw_withheld_tokens_from_accounts ( program_id, accounts, num_token_accounts)
236
305
}
237
306
TransferFeeInstruction :: HarvestWithheldTokensToMint => {
238
307
msg ! ( "TransferFeeInstruction: HarvestWithheldTokensToMint" ) ;
0 commit comments