Skip to content

Commit 7964b73

Browse files
committed
fix: top up or rent transfer mint to ctoken
1 parent bae9746 commit 7964b73

File tree

2 files changed

+74
-15
lines changed

2 files changed

+74
-15
lines changed

programs/compressed-token/program/src/mint_action/actions/mint_to_ctoken.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ pub fn process_mint_to_ctoken_action(
2121
validated_accounts: &MintActionAccounts,
2222
packed_accounts: &ProgramPackedAccounts<'_, AccountInfo>,
2323
mint: Pubkey,
24-
) -> Result<(), ProgramError> {
24+
) -> Result<Option<(u8, u64)>, ProgramError> {
2525
check_authority(
2626
compressed_mint.base.mint_authority,
2727
validated_accounts.authority.key(),
@@ -54,10 +54,12 @@ pub fn process_mint_to_ctoken_action(
5454
token_account_info,
5555
packed_accounts,
5656
);
57-
// For mint_to_ctoken, we don't need to handle lamport transfers
58-
// as there's no compressible extension on the target account
59-
compress_or_decompress_ctokens(inputs)?;
60-
Ok(())
57+
58+
// Capture top-up lamport amount if compressible extension present
59+
let transfer_amount = compress_or_decompress_ctokens(inputs)?;
60+
61+
// Return account index and amount if there's a transfer needed
62+
Ok(transfer_amount.map(|amount| (action.recipient.account_index, amount)))
6163
}
6264

6365
#[profile]

programs/compressed-token/program/src/mint_action/actions/process_actions.rs

Lines changed: 67 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use anchor_compressed_token::ErrorCode;
22
use anchor_lang::prelude::ProgramError;
3+
use arrayvec::ArrayVec;
34
use light_account_checks::packed_accounts::ProgramPackedAccounts;
45
use light_compressed_account::instruction_data::data::ZOutputCompressedAccountWithPackedContextMut;
56
use light_ctoken_types::{
@@ -9,16 +10,23 @@ use light_ctoken_types::{
910
};
1011
use light_program_profiler::profile;
1112
use pinocchio::account_info::AccountInfo;
13+
use spl_pod::solana_msg::msg;
1214

13-
use crate::mint_action::{
14-
accounts::MintActionAccounts,
15-
check_authority,
16-
mint_to::process_mint_to_compressed_action,
17-
mint_to_ctoken::process_mint_to_ctoken_action,
18-
queue_indices::QueueIndices,
19-
update_metadata::{
20-
process_remove_metadata_key_action, process_update_metadata_authority_action,
21-
process_update_metadata_field_action,
15+
use crate::{
16+
mint_action::{
17+
accounts::MintActionAccounts,
18+
check_authority,
19+
mint_to::process_mint_to_compressed_action,
20+
mint_to_ctoken::process_mint_to_ctoken_action,
21+
queue_indices::QueueIndices,
22+
update_metadata::{
23+
process_remove_metadata_key_action, process_update_metadata_authority_action,
24+
process_update_metadata_field_action,
25+
},
26+
},
27+
shared::{
28+
convert_program_error,
29+
transfer_lamports::{multi_transfer_lamports, Transfer},
2230
},
2331
};
2432

@@ -35,6 +43,9 @@ pub fn process_actions<'a>(
3543
packed_accounts: &ProgramPackedAccounts<'_, AccountInfo>,
3644
compressed_mint: &mut CompressedMint,
3745
) -> Result<(), ProgramError> {
46+
// Array to accumulate transfer amounts by account index (max 40 packed accounts)
47+
let mut transfer_map = [0u64; 40];
48+
3849
// Start metadata authority with same value as mint authority
3950
for action in parsed_instruction_data.actions.iter() {
4051
match action {
@@ -80,13 +91,27 @@ pub fn process_actions<'a>(
8091
// compressed_mint.metadata.spl_mint_initialized = true;
8192
}
8293
ZAction::MintToCToken(mint_to_ctoken_action) => {
83-
process_mint_to_ctoken_action(
94+
let transfer = process_mint_to_ctoken_action(
8495
mint_to_ctoken_action,
8596
compressed_mint,
8697
validated_accounts,
8798
packed_accounts,
8899
parsed_instruction_data.mint.metadata.mint,
89100
)?;
101+
102+
// Accumulate transfer amount if present (deduplication happens here)
103+
if let Some((account_index, amount)) = transfer {
104+
if account_index as usize > 40 {
105+
msg!(
106+
"Too many compression transfers: {}, max 40 allowed",
107+
account_index
108+
);
109+
return Err(ErrorCode::TooManyCompressionTransfers.into());
110+
}
111+
transfer_map[account_index as usize] = transfer_map[account_index as usize]
112+
.checked_add(amount)
113+
.ok_or(ProgramError::ArithmeticOverflow)?;
114+
}
90115
}
91116
ZAction::UpdateMetadataField(update_metadata_action) => {
92117
process_update_metadata_field_action(
@@ -112,5 +137,37 @@ pub fn process_actions<'a>(
112137
}
113138
}
114139

140+
// Build transfers array from deduplicated map
141+
let transfers: ArrayVec<Transfer, 40> = transfer_map
142+
.iter()
143+
.enumerate()
144+
.filter_map(|(index, &amount)| {
145+
if amount != 0 {
146+
Some((index as u8, amount))
147+
} else {
148+
None
149+
}
150+
})
151+
.map(|(index, amount)| {
152+
Ok(Transfer {
153+
account: packed_accounts.get_u8(index, "transfer account")?,
154+
amount,
155+
})
156+
})
157+
.collect::<Result<ArrayVec<Transfer, 40>, ProgramError>>()?;
158+
159+
// Execute transfers if any exist
160+
if !transfers.is_empty() {
161+
let fee_payer = validated_accounts
162+
.executing
163+
.as_ref()
164+
.and_then(|exec| Some(exec.system.fee_payer))
165+
.ok_or_else(|| {
166+
msg!("Fee payer required for compressible token account top-ups");
167+
ProgramError::NotEnoughAccountKeys
168+
})?;
169+
multi_transfer_lamports(fee_payer, &transfers).map_err(convert_program_error)?;
170+
}
171+
115172
Ok(())
116173
}

0 commit comments

Comments
 (0)