use crate::{id, vest_state::VestState};
use bincode::serialized_size;
use chrono::prelude::{Date, DateTime, Utc};
use num_derive::FromPrimitive;
use serde_derive::{Deserialize, Serialize};
use solana_sdk::{
decode_error::DecodeError,
instruction::{AccountMeta, Instruction, InstructionError},
pubkey::Pubkey,
system_instruction,
};
use thiserror::Error;
#[derive(Error, Debug, Clone, PartialEq, FromPrimitive)]
pub enum VestError {
#[error("destination missing")]
DestinationMissing,
#[error("unauthorized")]
Unauthorized,
}
impl From<VestError> for InstructionError {
fn from(e: VestError) -> Self {
InstructionError::Custom(e as u32)
}
}
impl<T> DecodeError<T> for VestError {
fn type_of() -> &'static str {
"VestError"
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum VestInstruction {
InitializeAccount {
terminator_pubkey: Pubkey,
payee_pubkey: Pubkey,
start_date_time: DateTime<Utc>,
date_pubkey: Pubkey,
total_lamports: u64,
},
SetTerminator(Pubkey),
SetPayee(Pubkey),
RedeemTokens,
Terminate,
Renege(u64),
VestAll,
}
fn initialize_account(
terminator_pubkey: &Pubkey,
payee_pubkey: &Pubkey,
contract_pubkey: &Pubkey,
start_date: Date<Utc>,
date_pubkey: &Pubkey,
total_lamports: u64,
) -> Instruction {
let keys = vec![AccountMeta::new(*contract_pubkey, false)];
Instruction::new(
id(),
&VestInstruction::InitializeAccount {
terminator_pubkey: *terminator_pubkey,
payee_pubkey: *payee_pubkey,
start_date_time: start_date.and_hms(0, 0, 0),
date_pubkey: *date_pubkey,
total_lamports,
},
keys,
)
}
pub fn create_account(
payer_pubkey: &Pubkey,
terminator_pubkey: &Pubkey,
contract_pubkey: &Pubkey,
payee_pubkey: &Pubkey,
start_date: Date<Utc>,
date_pubkey: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
let space = serialized_size(&VestState::default()).unwrap();
vec![
system_instruction::create_account(&payer_pubkey, contract_pubkey, lamports, space, &id()),
initialize_account(
terminator_pubkey,
payee_pubkey,
contract_pubkey,
start_date,
date_pubkey,
lamports,
),
]
}
pub fn set_terminator(contract: &Pubkey, old_pubkey: &Pubkey, new_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*contract, false),
AccountMeta::new(*old_pubkey, true),
];
Instruction::new(
id(),
&VestInstruction::SetTerminator(*new_pubkey),
account_metas,
)
}
pub fn set_payee(contract: &Pubkey, old_pubkey: &Pubkey, new_pubkey: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*contract, false),
AccountMeta::new(*old_pubkey, true),
];
Instruction::new(id(), &VestInstruction::SetPayee(*new_pubkey), account_metas)
}
pub fn redeem_tokens(contract: &Pubkey, date_pubkey: &Pubkey, to: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*contract, false),
AccountMeta::new_readonly(*date_pubkey, false),
AccountMeta::new(*to, false),
];
Instruction::new(id(), &VestInstruction::RedeemTokens, account_metas)
}
pub fn terminate(contract: &Pubkey, from: &Pubkey, to: &Pubkey) -> Instruction {
let mut account_metas = vec![
AccountMeta::new(*contract, false),
AccountMeta::new(*from, true),
];
if from != to {
account_metas.push(AccountMeta::new(*to, false));
}
Instruction::new(id(), &VestInstruction::Terminate, account_metas)
}
pub fn renege(contract: &Pubkey, from: &Pubkey, to: &Pubkey, lamports: u64) -> Instruction {
let mut account_metas = vec![
AccountMeta::new(*contract, false),
AccountMeta::new(*from, true),
];
if from != to {
account_metas.push(AccountMeta::new(*to, false));
}
Instruction::new(id(), &VestInstruction::Renege(lamports), account_metas)
}
pub fn vest_all(contract: &Pubkey, from: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new(*contract, false),
AccountMeta::new(*from, true),
];
Instruction::new(id(), &VestInstruction::VestAll, account_metas)
}