use crate::instruction::*;
use crate::pda::*;
use solana_program::{
instruction::{AccountMeta, Instruction},
program_error::ProgramError,
pubkey::Pubkey,
sysvar,
};
use spl_associated_token_account::get_associated_token_address;
pub fn initialize(
initializer: &Pubkey,
admin: Pubkey,
mint_fee_lamports: u64,
allocation_fee_per_recipient_lamports: u64,
merkle_fee_per_recipient_lamports: u64,
direct_transfer_fee_per_recipient_lamports: u64,
) -> Result<Instruction, ProgramError> {
let (config_pda, _) = config_pda();
let (fee_account_pda, _) = fee_account_pda();
let data = Initialize {
admin,
mint_fee_lamports: mint_fee_lamports.to_le_bytes(),
allocation_fee_per_recipient_lamports: allocation_fee_per_recipient_lamports.to_le_bytes(),
merkle_fee_per_recipient_lamports: merkle_fee_per_recipient_lamports.to_le_bytes(),
direct_transfer_fee_per_recipient_lamports: direct_transfer_fee_per_recipient_lamports
.to_le_bytes(),
};
let accounts = vec![
AccountMeta::new(*initializer, true),
AccountMeta::new(config_pda, false),
AccountMeta::new(fee_account_pda, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(sysvar::rent::ID, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data: data.to_bytes().to_vec(),
})
}
pub fn create_mint(
payer: &Pubkey,
mint_id: [u8; 32],
noise: [u8; 4],
mint: Pubkey, name: &[u8],
symbol: &[u8],
uri: &[u8],
decimals: u8,
max_supply: u64,
) -> Result<Instruction, ProgramError> {
let (config_pda, _) = config_pda();
let (fee_account_pda, _) = fee_account_pda();
let (mint_pda, _) = mint_pda(&mint_id, &noise);
let (mint_authority_pda, _) = mint_authority_pda(&mint_pda);
let (mint_treasury_pda, _) = mint_treasury_pda(&mint_pda);
let (mint_metadata_pda, _) = Pubkey::find_program_address(
&[
b"metadata",
mpl_token_metadata::ID.as_ref(),
mint_pda.as_ref(),
],
&mpl_token_metadata::ID,
);
let mint_treasury_tokens = get_associated_token_address(&mint_treasury_pda, &mint_pda);
let mut name_padded = [0u8; 32];
let name_len = name.len().min(32);
name_padded[..name_len].copy_from_slice(&name[..name_len]);
let mut symbol_padded = [0u8; 10];
let symbol_len = symbol.len().min(10);
symbol_padded[..symbol_len].copy_from_slice(&symbol[..symbol_len]);
let mut uri_padded = [0u8; 128];
let uri_len = uri.len().min(128);
uri_padded[..uri_len].copy_from_slice(&uri[..uri_len]);
let data = CreateMint {
mint_id,
noise,
mint: if mint == Pubkey::default() {
mint_pda
} else {
mint
},
name: name_padded,
symbol: symbol_padded,
uri: uri_padded,
decimals,
max_supply: max_supply.to_le_bytes(),
_padding: [0u8; 3],
};
let accounts = vec![
AccountMeta::new(*payer, true),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(mint_pda, false),
AccountMeta::new_readonly(mint_authority_pda, false),
AccountMeta::new(mint_metadata_pda, false),
AccountMeta::new_readonly(mint_treasury_pda, false),
AccountMeta::new(mint_treasury_tokens, false),
AccountMeta::new(fee_account_pda, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new_readonly(spl_associated_token_account::ID, false),
AccountMeta::new_readonly(mpl_token_metadata::ID, false),
AccountMeta::new_readonly(sysvar::rent::ID, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data: data.to_bytes().to_vec(),
})
}
pub fn create_campaign(
payer: &Pubkey,
campaign_owner: &Pubkey,
campaign_id: [u8; 32],
mint: Pubkey,
merkle_root: [u8; 32],
recipient_count: u64,
initial_supply: u64,
max_supply: u64,
) -> Result<Instruction, ProgramError> {
let (config_pda, _) = config_pda();
let (fee_account_pda, _) = fee_account_pda();
let (campaign_pda, _) = campaign_pda(&campaign_id);
let (mint_treasury_pda, _) = mint_treasury_pda(&mint);
let (campaign_treasury_pda, _) = campaign_treasury_pda(&campaign_id);
let mint_treasury_tokens = get_associated_token_address(&mint_treasury_pda, &mint);
let campaign_treasury_tokens = get_associated_token_address(&campaign_treasury_pda, &mint);
let data = CreateCampaign {
campaign_id,
mint,
merkle_root,
recipient_count: recipient_count.to_le_bytes(),
initial_supply: initial_supply.to_le_bytes(),
max_supply: max_supply.to_le_bytes(),
_padding: [0u8; 7],
};
let accounts = vec![
AccountMeta::new(*payer, true),
AccountMeta::new(*campaign_owner, true),
AccountMeta::new_readonly(config_pda, false),
AccountMeta::new(campaign_pda, false),
AccountMeta::new_readonly(mint, false),
AccountMeta::new_readonly(mint_treasury_pda, false),
AccountMeta::new(mint_treasury_tokens, false),
AccountMeta::new_readonly(campaign_treasury_pda, false),
AccountMeta::new(campaign_treasury_tokens, false),
AccountMeta::new(fee_account_pda, false),
AccountMeta::new_readonly(spl_token::ID, false),
AccountMeta::new_readonly(spl_associated_token_account::ID, false),
AccountMeta::new_readonly(solana_program::system_program::ID, false),
AccountMeta::new_readonly(sysvar::rent::ID, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data: data.to_bytes().to_vec(),
})
}
pub fn direct_transfer(
owner: &Pubkey,
campaign_id: &[u8; 32],
campaign: &Pubkey,
config: &Pubkey,
fee_account: &Pubkey,
treasury: &Pubkey,
treasury_tokens: &Pubkey,
mint: &Pubkey,
token_program: &Pubkey,
associated_token_program: &Pubkey,
system_program: &Pubkey,
transfers: Vec<(Pubkey, u64)>,
) -> Result<Instruction, ProgramError> {
if transfers.is_empty() || transfers.len() > 10 {
return Err(ProgramError::InvalidInstructionData);
}
let mut data = DirectTransfer {
campaign_id: *campaign_id,
count: transfers.len() as u8,
_padding: [0u8; 7],
}
.to_bytes()
.to_vec();
for (recipient, amount) in &transfers {
data.extend_from_slice(recipient.as_ref());
data.extend_from_slice(&amount.to_le_bytes());
}
let mut accounts = vec![
AccountMeta::new(*owner, true),
AccountMeta::new_readonly(*campaign, false),
AccountMeta::new_readonly(*config, false),
AccountMeta::new(*fee_account, false),
AccountMeta::new_readonly(*treasury, false),
AccountMeta::new(*treasury_tokens, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*token_program, false),
AccountMeta::new_readonly(*associated_token_program, false),
AccountMeta::new_readonly(*system_program, false),
];
for (recipient, _) in &transfers {
let recipient_tokens = get_associated_token_address(recipient, mint);
accounts.push(AccountMeta::new(recipient_tokens, false));
}
Ok(Instruction {
program_id: crate::id(),
accounts,
data,
})
}
pub fn replenish(
campaign_owner: &Pubkey,
campaign_id: &[u8; 32],
campaign: &Pubkey,
mint: &Pubkey,
mint_treasury: &Pubkey,
mint_treasury_tokens: &Pubkey,
campaign_treasury: &Pubkey,
campaign_treasury_tokens: &Pubkey,
token_program: &Pubkey,
amount: u64,
) -> Result<Instruction, ProgramError> {
let data = Replenish {
campaign_id: *campaign_id,
amount: amount.to_le_bytes(),
_padding: [0u8; 7],
};
let accounts = vec![
AccountMeta::new(*campaign_owner, true),
AccountMeta::new(*campaign, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*mint_treasury, false),
AccountMeta::new(*mint_treasury_tokens, false),
AccountMeta::new_readonly(*campaign_treasury, false),
AccountMeta::new(*campaign_treasury_tokens, false),
AccountMeta::new_readonly(*token_program, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data: data.to_bytes().to_vec(),
})
}
pub fn update_config(
admin: &Pubkey,
new_admin: Pubkey, mint_fee_lamports: u64, allocation_fee_per_recipient_lamports: u64, merkle_fee_per_recipient_lamports: u64, direct_transfer_fee_per_recipient_lamports: u64, ) -> Result<Instruction, ProgramError> {
let (config_pda, _) = config_pda();
let data = UpdateConfig {
new_admin,
mint_fee_lamports: mint_fee_lamports.to_le_bytes(),
allocation_fee_per_recipient_lamports: allocation_fee_per_recipient_lamports.to_le_bytes(),
merkle_fee_per_recipient_lamports: merkle_fee_per_recipient_lamports.to_le_bytes(),
direct_transfer_fee_per_recipient_lamports: direct_transfer_fee_per_recipient_lamports
.to_le_bytes(),
};
let accounts = vec![
AccountMeta::new(*admin, true),
AccountMeta::new(config_pda, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data: data.to_bytes().to_vec(),
})
}
pub fn withdraw_fees(
admin: &Pubkey,
config: &Pubkey,
fee_account: &Pubkey,
beneficiary: &Pubkey,
system_program: &Pubkey,
amount: u64, ) -> Result<Instruction, ProgramError> {
let data = WithdrawFees {
amount: amount.to_le_bytes(),
};
let accounts = vec![
AccountMeta::new(*admin, true),
AccountMeta::new_readonly(*config, false),
AccountMeta::new(*fee_account, false),
AccountMeta::new(*beneficiary, false),
AccountMeta::new_readonly(*system_program, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data: data.to_bytes().to_vec(),
})
}
pub fn allocate_onchain(
owner: &Pubkey,
campaign_id: &[u8; 32],
campaign: &Pubkey,
config: &Pubkey,
fee_account: &Pubkey,
treasury_tokens: &Pubkey,
token_program: &Pubkey,
system_program: &Pubkey,
allocations: Vec<(Pubkey, u64)>,
) -> Result<Instruction, ProgramError> {
let mut data = Allocate {
campaign_id: *campaign_id,
count: allocations.len() as u8,
_padding: [0u8; 7],
}
.to_bytes()
.to_vec();
for (recipient, amount) in &allocations {
data.extend_from_slice(recipient.as_ref());
data.extend_from_slice(&amount.to_le_bytes());
}
let mut accounts = vec![
AccountMeta::new(*owner, true),
AccountMeta::new(*campaign, false),
AccountMeta::new_readonly(*config, false),
AccountMeta::new(*fee_account, false),
AccountMeta::new(*treasury_tokens, false),
AccountMeta::new_readonly(*token_program, false),
AccountMeta::new_readonly(*system_program, false),
];
for (recipient, _) in &allocations {
let (allocation_pda, _) = crate::pda::allocation_pda(*campaign, *recipient);
accounts.push(AccountMeta::new(allocation_pda, false));
}
Ok(Instruction {
program_id: crate::id(),
accounts,
data,
})
}
pub fn claim_onchain(
recipient: &Pubkey,
campaign_id: &[u8; 32],
campaign: &Pubkey,
allocation: &Pubkey,
treasury: &Pubkey,
treasury_tokens: &Pubkey,
recipient_tokens: &Pubkey,
mint: &Pubkey,
token_program: &Pubkey,
associated_token_program: &Pubkey,
system_program: &Pubkey,
) -> Result<Instruction, ProgramError> {
let data = Claim {
campaign_id: *campaign_id,
method: 0, amount: [0u8; 8],
merkle_proof_length: 0,
_padding: [0u8; 6],
};
let accounts = vec![
AccountMeta::new(*recipient, true),
AccountMeta::new_readonly(*campaign, false),
AccountMeta::new(*allocation, false),
AccountMeta::new_readonly(*treasury, false), AccountMeta::new(*treasury_tokens, false),
AccountMeta::new(*recipient_tokens, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*token_program, false),
AccountMeta::new_readonly(*associated_token_program, false),
AccountMeta::new_readonly(*system_program, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data: data.to_bytes().to_vec(),
})
}
pub fn claim_offchain(
recipient: &Pubkey,
campaign_id: &[u8; 32],
campaign: &Pubkey,
allocation: &Pubkey, treasury: &Pubkey,
treasury_tokens: &Pubkey,
recipient_tokens: &Pubkey,
mint: &Pubkey,
token_program: &Pubkey,
associated_token_program: &Pubkey,
system_program: &Pubkey,
amount: u64,
signature: &[u8; 64],
) -> Result<Instruction, ProgramError> {
let mut data = Claim {
campaign_id: *campaign_id,
method: 1, amount: amount.to_le_bytes(),
merkle_proof_length: 0,
_padding: [0u8; 6],
}
.to_bytes()
.to_vec();
data.extend_from_slice(signature);
let accounts = vec![
AccountMeta::new(*recipient, true),
AccountMeta::new_readonly(*campaign, false),
AccountMeta::new_readonly(*allocation, false), AccountMeta::new_readonly(*treasury, false), AccountMeta::new(*treasury_tokens, false),
AccountMeta::new(*recipient_tokens, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*token_program, false),
AccountMeta::new_readonly(*associated_token_program, false),
AccountMeta::new_readonly(*system_program, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data,
})
}
pub fn claim_merkle(
recipient: &Pubkey,
campaign_id: &[u8; 32],
campaign: &Pubkey,
allocation: &Pubkey, treasury: &Pubkey,
treasury_tokens: &Pubkey,
recipient_tokens: &Pubkey,
mint: &Pubkey,
token_program: &Pubkey,
associated_token_program: &Pubkey,
system_program: &Pubkey,
amount: u64,
proof_path: &[[u8; 32]],
proof_indices: &[bool],
) -> Result<Instruction, ProgramError> {
if proof_path.len() != proof_indices.len() {
return Err(ProgramError::InvalidInstructionData);
}
if proof_path.len() > crate::consts::MAX_MERKLE_PROOF_LENGTH as usize {
return Err(ProgramError::InvalidInstructionData);
}
let proof_length = proof_path.len() as u8;
let mut data = Claim {
campaign_id: *campaign_id,
method: 2, amount: amount.to_le_bytes(),
merkle_proof_length: proof_length,
_padding: [0u8; 6],
}
.to_bytes()
.to_vec();
for hash in proof_path {
data.extend_from_slice(hash);
}
for &is_right in proof_indices {
data.push(if is_right { 1 } else { 0 });
}
let accounts = vec![
AccountMeta::new(*recipient, true),
AccountMeta::new_readonly(*campaign, false),
AccountMeta::new_readonly(*allocation, false), AccountMeta::new_readonly(*treasury, false), AccountMeta::new(*treasury_tokens, false),
AccountMeta::new(*recipient_tokens, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*token_program, false),
AccountMeta::new_readonly(*associated_token_program, false),
AccountMeta::new_readonly(*system_program, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data,
})
}
pub fn mint_tokens(
campaign_owner: &Pubkey,
campaign_id: &[u8; 32],
campaign: &Pubkey,
mint: &Pubkey,
mint_authority: &Pubkey,
treasury_tokens: &Pubkey,
token_program: &Pubkey,
amount: u64,
) -> Result<Instruction, ProgramError> {
let data = MintTokens {
campaign_id: *campaign_id,
amount: amount.to_le_bytes(),
_padding: [0u8; 7],
};
let accounts = vec![
AccountMeta::new(*campaign_owner, true),
AccountMeta::new(*campaign, false),
AccountMeta::new_readonly(*mint, false),
AccountMeta::new_readonly(*mint_authority, false),
AccountMeta::new(*treasury_tokens, false),
AccountMeta::new_readonly(*token_program, false),
];
Ok(Instruction {
program_id: crate::id(),
accounts,
data: data.to_bytes().to_vec(),
})
}