Skip to content

Commit 66827ca

Browse files
authored
Add allocate impl for cpi instruction (#20)
* Add allocate impl for cpi instruction * Fix typos
1 parent 04184f9 commit 66827ca

File tree

2 files changed

+134
-5
lines changed

2 files changed

+134
-5
lines changed

clients/rust/asset/src/impls.rs

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
use crate::instructions::AllocateCpiAccounts;
2+
3+
impl<'a, 'b> AllocateCpiAccounts<'a, 'b> {
4+
/// Invokes the `Allocate` instruction.
5+
///
6+
/// This invocation expects the instruction data to be directly provided, which
7+
/// will save CU compared to a Borsh equivalent. In most cases, the instruction
8+
/// data will be generated using `allocate_instruction_data!` macro.
9+
#[inline(always)]
10+
pub fn invoke(
11+
&self,
12+
program: &'b solana_program::account_info::AccountInfo<'a>,
13+
instruction_data: Vec<u8>,
14+
) -> solana_program::entrypoint::ProgramResult {
15+
self.invoke_signed(program, instruction_data, &[])
16+
}
17+
18+
/// Invokes the `Allocate` instruction with the specified signers.
19+
///
20+
/// This invocation expects the instruction data to be directly provided, which
21+
/// will save CU compared to a Borsh equivalent. In most cases, the instruction
22+
/// data will be generated using `allocate_instruction_data!` macro.
23+
#[inline(always)]
24+
pub fn invoke_signed(
25+
&self,
26+
program: &'b solana_program::account_info::AccountInfo<'a>,
27+
instruction_data: Vec<u8>,
28+
signers_seeds: &[&[&[u8]]],
29+
) -> solana_program::entrypoint::ProgramResult {
30+
let accounts = vec![
31+
solana_program::instruction::AccountMeta::new(*self.asset.key, true),
32+
if let Some(payer) = self.payer {
33+
solana_program::instruction::AccountMeta::new(*payer.key, true)
34+
} else {
35+
solana_program::instruction::AccountMeta::new_readonly(crate::ASSET_ID, false)
36+
},
37+
if let Some(system_program) = self.system_program {
38+
solana_program::instruction::AccountMeta::new_readonly(*system_program.key, false)
39+
} else {
40+
solana_program::instruction::AccountMeta::new_readonly(crate::ASSET_ID, false)
41+
},
42+
];
43+
44+
let instruction = solana_program::instruction::Instruction {
45+
program_id: crate::ASSET_ID,
46+
accounts,
47+
data: instruction_data,
48+
};
49+
let mut account_infos = Vec::with_capacity(4);
50+
account_infos.push(program.clone());
51+
account_infos.push(self.asset.clone());
52+
if let Some(payer) = self.payer {
53+
account_infos.push(payer.clone());
54+
}
55+
if let Some(system_program) = self.system_program {
56+
account_infos.push(system_program.clone());
57+
}
58+
59+
if signers_seeds.is_empty() {
60+
solana_program::program::invoke(&instruction, &account_infos)
61+
} else {
62+
solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
63+
}
64+
}
65+
}
66+
67+
/// Convenience macro to create the instruction data for the `Allocate` instruction.
68+
///
69+
/// There are two forms of this macro and they vary based on how the extension data
70+
/// is provided:
71+
///
72+
/// 1. You can provide the data directly as a byte slice;
73+
/// 2. You can provide the length of the data and the instruction data will have the
74+
/// correct capacity. This is useful when you want to write the data (bytes) iteratively
75+
/// without having to allocate a new `Vec` for it.
76+
///
77+
/// # Arguments
78+
///
79+
/// 1. `extension_type` - the type ([`ExtensionType`]) of the extension.
80+
/// 2. `length` - expression representing the length of the extension data.
81+
/// 3. `data` - (optional) the extension data as a byte slice.
82+
#[macro_export]
83+
macro_rules! allocate_instruction_data {
84+
( $extension_type:expr, $length:expr, $data:tt ) => {{
85+
let discriminator: u8 = 4;
86+
87+
let mut size = std::mem::size_of::<u8>() // discriminator
88+
+ std::mem::size_of::<u8>() // extension type
89+
+ std::mem::size_of::<u32>() // length
90+
+ std::mem::size_of::<u8>() // option
91+
+ std::mem::size_of::<u32>() // data length
92+
+ $data.len(); // data
93+
94+
let mut instruction_data = Vec::with_capacity(size);
95+
instruction_data.push(discriminator);
96+
instruction_data.push($extension_type as u8);
97+
instruction_data.extend_from_slice(&u32::to_le_bytes($length as u32));
98+
instruction_data.push(1);
99+
instruction_data.extend_from_slice(&u32::to_le_bytes($data.len() as u32));
100+
instruction_data.extend_from_slice($data);
101+
102+
instruction_data
103+
}};
104+
105+
( $extension_type:expr, $length:expr ) => {{
106+
let discriminator: u8 = 4;
107+
108+
let mut size = std::mem::size_of::<u8>() // discriminator
109+
+ std::mem::size_of::<u8>() // extension type
110+
+ std::mem::size_of::<u32>() // length
111+
+ std::mem::size_of::<u8>() // option
112+
+ std::mem::size_of::<u32>() // data length
113+
+ $length as usize; // allocated data space
114+
115+
let mut instruction_data = Vec::with_capacity(size);
116+
instruction_data.push(discriminator);
117+
instruction_data.push($extension_type as u8);
118+
instruction_data.extend_from_slice(&u32::to_le_bytes($length as u32));
119+
instruction_data.push(1);
120+
instruction_data.extend_from_slice(&u32::to_le_bytes($length as u32));
121+
122+
instruction_data
123+
}};
124+
}

clients/rust/asset/src/lib.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
mod generated;
2+
mod impls;
23
mod mint;
4+
5+
pub use generated::programs::ASSET_ID as ID;
6+
pub use generated::*;
7+
pub use mint::*;
8+
9+
// Re-export nifty_asset_types for convenience
10+
pub mod constraints {
11+
pub use nifty_asset_types::constraints::*;
12+
}
313
pub mod extensions {
414
pub use nifty_asset_types::extensions::*;
515
}
616
pub mod state {
717
pub use nifty_asset_types::state::*;
818
}
9-
10-
pub use generated::programs::ASSET_ID as ID;
11-
pub use generated::*;
12-
13-
pub use mint::*;
1419
pub use nifty_asset_types::podded::ZeroCopy;

0 commit comments

Comments
 (0)