| Crates.io | pina_macros |
| lib.rs | pina_macros |
| version | 0.2.0 |
| created_at | 2025-11-08 12:34:55.388782+00 |
| updated_at | 2025-12-13 12:02:38.12169+00 |
| description | derive macros for a solana and pinocchio smart contract framework |
| homepage | https://pina.rs |
| repository | https://github.com/pina-rs/pina |
| max_upload_size | |
| id | 1922823 |
| size | 66,029 |
pina_macrosDerive, Attribute and Funtion macros which are used to make development with pina easier.
#[discriminator]This attribute macro should be used for annotating the globally shared instruction and account discriminators.
primitive - Defaults to u8 which takes up 1 byte of space for the discriminator. This would allow up to 256 variations of the type being discriminated. The type can be the following:
u8 - 256 variationsu16 - 65,536 variationsu32 - 4,294,967,296 variationsu64 - 18,446,744,073,709,551,616 variations (overkill!)crate - this defaults to ::pina as the developer is expected to have access to the pina crate in the dependencies.final - By default all discriminator enums are marked as non_exhaustive. The final flag will remove this annotation.The following:
use pina::*;
#[discriminator(crate = ::pina, primitive = u8, final)]
pub enum MyAccount {
ConfigState = 0,
GameState = 1,
SectionState = 2,
}
Is transformed to:
use pina::*;
#[repr(u8)]
#[derive(
::core::fmt::Debug,
::core::clone::Clone,
::core::marker::Copy,
::core::cmp::PartialEq,
::core::cmp::Eq,
)]
pub enum MyAccount {
ConfigState = 0,
GameState = 1,
SectionState = 2,
}
impl ::core::convert::From<MyAccount> for u8 {
#[inline]
fn from(enum_value: TryIt) -> Self {
enum_value as Self
}
}
impl ::core::convert::TryFrom<u8> for MyAccount {
type Error = ::pina::ProgramError;
#[inline]
fn try_from(number: u8) -> ::core::result::Result<Self, ::pina::ProgramError> {
#![allow(non_upper_case_globals)]
const __CONFIG_STATE: u8 = 0;
const __GAME_STATE: u8 = 1;
const __SECTION_STATE: u8 = 2;
#[deny(unreachable_patterns)]
match number {
__CONFIG_STATE => ::core::result::Result::Ok(Self::ConfigState),
__GAME_STATE => ::core::result::Result::Ok(Self::GameState),
__SECTION_STATE => ::core::result::Result::Ok(Self::SectionState),
#[allow(unreachable_patterns)]
_ => ::core::result::Result::Err(::pina::PinaProgramError::InvalidDiscriminator.into()),
}
}
}
unsafe impl Zeroable for MyAccount {}
unsafe impl Pod for MyAccount {}
::pina::into_discriminator!(MyAccount, u8);
#[account]The account macro is used to annotate account data that will exist within a solana account.
crate - this defaults to ::pina as the developer is expected to have access to the pina crate in the dependencies.discriminator - the discriminator enum to use for this account. The variant should match the name of the account struct.It will transform the following:
use pina::*;
#[discriminator(crate = ::pina, primitive = u8, final)]
pub enum MyAccount {
ConfigState = 0,
GameState = 1,
SectionState = 2,
}
#[account(crate = ::pina, discriminator = MyAccount)]
#[derive(Debug)]
pub struct ConfigState {
/// The version of the state.
pub version: u8,
/// The authority which can update this config.
pub authority: Pubkey,
/// Store the bump to save compute units.
pub bump: u8,
/// The treasury account bump where fees are sent and where the minted
/// tokens are transferred.
pub treasury_bump: u8,
/// The mint account bump.
pub mint_bit_bump: u8,
/// The mint account bump for KIBIBIT.
pub mint_kibibit_bump: u8,
/// The mint account bump for MEBIBIT.
pub mint_mebibit_bump: u8,
/// The mint account bump for GIBIBIT.
pub mint_gibibit_bump: u8,
/// There will be a maximum of 8 games.
pub game_index: u8,
}
Into:
use pina::*;
#[discriminator(crate = ::pina, primitive = u8, final)]
pub enum MyAccount {
ConfigState = 0,
GameState = 1,
SectionState = 2,
}
#[repr(C)]
#[derive(
Debug,
::core::clone::Clone,
::core::marker::Copy,
::core::cmp::PartialEq,
::core::cmp::Eq,
::pina::Pod,
::pina::Zeroable,
::pina::TypedBuilder,
)]
#[builder(builder_method(vis = "", name = __builder))]
#[bytemuck(crate = "::pina::bytemuck")]
pub struct ConfigState {
// This discriminator is automatically injected as the first field in the struct. It must be
// present.
discriminator: [u8; MyAccount::BYTES],
/// The version of the state.
pub version: u8,
/// The authority which can update this config.
pub authority: Pubkey,
/// Store the bump to save compute units.
pub bump: u8,
/// The treasury account bump where fees are sent and where the minted
/// tokens are transferred.
pub treasury_bump: u8,
/// The mint account bump.
pub mint_bit_bump: u8,
/// The mint account bump for KIBIBIT.
pub mint_kibibit_bump: u8,
/// The mint account bump for MEBIBIT.
pub mint_mebibit_bump: u8,
/// The mint account bump for GIBIBIT.
pub mint_gibibit_bump: u8,
/// There will be a maximum of 8 games.
pub game_index: u8,
}
// This type is generated to match the `TypedBuilder` type with the
// discriminator already set.
type ConfigStateBuilderType = ConfigStateBuilder<(
([u8; MyAccount::BYTES],), /* `discriminator`: automatically applied in the builder method
* below. */
(), // `version`
(), // `authority`
(), // `bump`
(), // `treasury_bump`
(), // `mint_bit_bump`
(), // `mint_kibibit_bump`
(), // `mint_mebibit_bump`
(), // `mint_gibibit_bump`
(), // `game_index`
)>;
impl ConfigState {
pub fn to_bytes(&self) -> &[u8] {
::pina::bytemuck::bytes_of(self)
}
pub fn builder() -> ConfigStateBuilderType {
let mut bytes = [0u8; MyAccount::BYTES];
<Self as ::pina::HasDiscriminator>::VALUE.write_discriminator(&mut bytes);
Self::__builder().discriminator(bytes)
}
}
impl ::pina::HasDiscriminator for ConfigState {
type Type = MyAccount;
const VALUE: Self::Type = MyAccount::ConfigState;
}
impl ::pina::AccountValidation for ConfigState {
#[track_caller]
fn assert<F>(&self, condition: F) -> Result<&Self, ::pina::ProgramError>
where
F: Fn(&Self) -> bool,
{
if condition(self) {
return Ok(self);
}
::pina::log!("Account is invalid");
::pina::log_caller();
Err(::pina::ProgramError::InvalidAccountData)
}
#[track_caller]
fn assert_msg<F>(&self, condition: F, msg: &str) -> Result<&Self, ::pina::ProgramError>
where
F: Fn(&Self) -> bool,
{
match ::pina::assert(
condition(self),
::pina::ProgramError::InvalidAccountData,
msg,
) {
Err(err) => Err(err),
Ok(()) => Ok(self),
}
}
#[track_caller]
fn assert_mut<F>(&mut self, condition: F) -> Result<&mut Self, ::pina::ProgramError>
where
F: Fn(&Self) -> bool,
{
if !condition(self) {
return Ok(self);
}
::pina::log!("Account is invalid");
::pina::log_caller();
Err(::pina::ProgramError::InvalidAccountData)
}
#[track_caller]
fn assert_mut_msg<F>(
&mut self,
condition: F,
msg: &str,
) -> Result<&mut Self, ::pina::ProgramError>
where
F: Fn(&Self) -> bool,
{
match ::pina::assert(
condition(self),
::pina::ProgramError::InvalidAccountData,
msg,
) {
Err(err) => Err(err),
Ok(()) => Ok(self),
}
}
}
#[instruction]The instruction macro is used to annotate instruction data that will exist within a solana instruction.
discriminator - the discriminator enum to use for this instruction. The variant should match the name of the instruction struct.It will transform the following:
use pina::*;
#[discriminator(crate = ::pina, primitive = u8, final)]
pub enum MyInstruction {
Add = 0,
FlipBit = 1,
}
#[instruction(crate = ::pina, discriminator = MyInstruction)]
#[derive(Debug)]
pub struct FlipBit {
/// The data section being updated.
pub section_index: u8,
/// The index of the `u16` value in the array.
pub array_index: u8,
/// The offset of the bit being set.
pub offset: u8,
/// The value to set the bit to: `0` or `1`.
pub value: u8,
}
Is transformed to:
use pina::*;
#[discriminator(crate = ::pina, primitive = u8, final)]
pub enum MyInstruction {
Add = 0,
FlipBit = 1,
}
#[repr(C)]
#[derive(
Debug,
::core::clone::Clone,
::core::marker::Copy,
::core::cmp::PartialEq,
::core::cmp::Eq,
::pina::Pod,
::pina::Zeroable,
::pina::TypedBuilder,
)]
#[builder(builder_method(vis = "", name = __builder))]
#[bytemuck(crate = "::pina::bytemuck")]
pub struct FlipBit {
// This discriminator is automatically injected as the first field in the struct. It must be
// present.
discriminator: [u8; MyInstruction::BYTES],
/// The data section being updated.
pub section_index: u8,
/// The index of the `u16` value in the array.
pub array_index: u8,
/// The offset of the bit being set.
pub offset: u8,
/// The value to set the bit to: `0` or `1`.
pub value: u8,
}
// This type is generated to match the `TypedBuilder` type with the
// discriminator already set.
type FlipBitBuilderType = FlipBitBuilder<(
([u8; MyInstruction::BYTES],), /* `discriminator`: automatically applied in the builder
* method below. */
(), // `section_index`
(), // `array_index`
(), // `offset`
(), // `value`
)>;
impl FlipBit {
pub fn to_bytes(&self) -> &[u8] {
::pina::bytemuck::bytes_of(self)
}
pub fn try_from_bytes(data: &[u8]) -> Result<&Self, ::pina::ProgramError> {
::pina::bytemuck::try_from_bytes::<Self>(data)
.or(Err(::pina::ProgramError::InvalidInstructionData))
}
pub fn builder() -> FlipBitBuilderType {
let mut bytes = [0u8; MyInstruction::BYTES];
<Self as ::pina::HasDiscriminator>::VALUE.write_discriminator(&mut bytes);
Self::__builder().discriminator(bytes)
}
}
impl ::pina::HasDiscriminator for FlipBit {
type Type = MyInstruction;
const VALUE: Self::Type = MyInstruction::FlipBit;
}
#[event]Annotates a struct as an event.
crate - this defaults to ::pina as the developer is expected to have access to the pina crate in the dependencies.discriminator - the discriminator enum to use for this event. The variant should match the name of the account struct.use pina::*;
#[discriminator(primitive = u8)]
pub enum Event {
Initialize = 0,
Abandon = 1,
}
#[event(discriminator = Event, variant = Initialize)]
pub struct InitializeEvent {
pub choice: u8,
}
Is transformed into:
use pina::*;
#[discriminator(primitive = u8)]
pub enum Event {
Initialize = 0,
Abandon = 1,
}
#[repr(C)]
#[derive(
::core::clone::Clone,
::core::marker::Copy,
::core::cmp::PartialEq,
::core::cmp::Eq,
::pina::Pod,
::pina::Zeroable,
::pina::TypedBuilder,
)]
#[builder(builder_method(vis = "", name = __builder))]
#[bytemuck(crate = "::pina::bytemuck")]
pub struct InitializeEvent {
// This discriminator is automatically injected as the first field in the struct. It must be
// present.
discriminator: [u8; Event::BYTES],
pub choice: u8,
}
// This type is generated to match the `TypedBuilder` type with the
// discriminator already set.
type InitializeEventBuilderType = InitializeEventBuilder<(
([u8; Event::BYTES],), /* `discriminator`: automatically applied in the builder
* method below. */
(), // `choice`
)>;
impl InitializeEvent {
pub fn to_bytes(&self) -> &[u8] {
::pina::bytemuck::bytes_of(self)
}
pub fn try_from_bytes(data: &[u8]) -> Result<&Self, ::pina::ProgramError> {
::pina::bytemuck::try_from_bytes::<Self>(data)
.or(Err(::pina::ProgramError::InvalidInstructionData))
}
pub fn builder() -> InitializeEventBuilderType {
let mut bytes = [0u8; Event::BYTES];
<Self as ::pina::HasDiscriminator>::VALUE.write_discriminator(&mut bytes);
Self::__builder().discriminator(bytes)
}
}
impl ::pina::HasDiscriminator for InitializeEvent {
type Type = MyInstruction;
const VALUE: Self::Type = MyInstruction::FlipBit;
}
#[error]#[error] is a lightweight modification to the provided enum acting as syntactic sugar to make it easier to manage your custom program errors.
crate - this defaults to ::pina as the developer is expected to have access to the pina crate in the dependencies.final - By default all error enums are marked as non_exhaustive. The final flag will remove this.use pina::*;
#[error(crate = ::pina)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MyError {
/// Doc comments are significant as they will be read by a future parse to
/// generte the IDL.
Invalid = 0,
/// A duplicate issue has occurred.
Duplicate = 1,
}
The above is transformed into:
#[non_exhaustive] // This is present if you haven't set the flag`final`.
#[derive(
::core::fmt::Debug,
::core::clone::Clone,
::core::marker::Copy,
::core::cmp::PartialEq,
::core::cmp::Eq,
)]
#[repr(u32)]
pub enum MyError {
/// Doc comments are significant as they will be read by a future parse to
/// generte the IDL.
Invalid = 0,
/// A duplicate issue has occurred.
Duplicate = 1,
}
impl ::core::convert::From<MyError> for ::pina::ProgramError {
fn from(e: MyError) -> Self {
::pina::pinocchio::program_error::ProgramError::Custom(e as u32)
}
}
#[derive(Accounts)]This adds a TryFrom implementation to a struct of AccountInfo's.
crate - this defaults to ::pina as the developer is expected to have access to the pina crate in the dependencies.remaining - a field level annotation that annotates the field as containing all the remaining accounts not specified in the struct. If not specified then the exact number of struct fields must be equal to the exact number of items in the provided AccountInfo slice.use pina::*;
#[derive(Accounts)]
#[pina(crate = ::pina)]
pub struct MakeOfferAccounts<'a> {
pub maker: &'a AccountInfo,
pub token_mint_a: &'a AccountInfo,
pub token_mint_b: &'a AccountInfo,
pub maker_ata_a: &'a AccountInfo,
pub offer: &'a AccountInfo,
pub vault: &'a AccountInfo,
pub token_program: &'a AccountInfo,
// If this is not present then the struct expects to consume all provided accounts.
#[pina(remaining)]
pub remaining: &'a [AccountInfo],
}
Into:
use pina::*;
pub struct MakeOfferAccounts<'a> {
pub maker: &'a AccountInfo,
pub token_mint_a: &'a AccountInfo,
pub token_mint_b: &'a AccountInfo,
pub maker_ata_a: &'a AccountInfo,
pub offer: &'a AccountInfo,
pub vault: &'a AccountInfo,
pub token_program: &'a AccountInfo,
pub remaining: &'a [AccountInfo],
}
impl<'a> ::pina::TryFromAccountInfos<'a> for MakeOfferAccounts<'a> {
fn try_from_account_infos(
accounts: &'a [::pina::AccountInfo],
) -> ::core::result::Result<Self, ::pina::ProgramError> {
let [
maker,
token_mint_a,
token_mint_b,
maker_ata_a,
offer,
vault,
token_program,
remaining @ ..,
] = accounts
else {
return ::core::result::Result::Err(::pina::ProgramError::NotEnoughAccountKeys);
};
Ok(Self {
maker,
token_mint_a,
token_mint_b,
maker_ata_a,
offer,
vault,
token_program,
remaining,
})
}
}
impl<'a> ::core::convert::TryFrom<&'a [::pina::AccountInfo]> for MakeOfferAccounts<'a> {
type Error = ::pina::ProgramError;
fn try_from(accounts: &'a [::pina::AccountInfo]) -> ::core::result::Result<Self, Self::Error> {
<Self as ::pina::TryFromAccountInfos>::try_from_account_infos(accounts)
}
}