solana/programs/vest/src/vest_state.rs

111 lines
3.4 KiB
Rust
Raw Normal View History

2019-10-04 14:43:50 -07:00
//! vest state
use crate::vest_schedule::create_vesting_schedule;
use bincode::{self, deserialize, serialize_into};
use chrono::prelude::*;
use chrono::{
prelude::{DateTime, TimeZone, Utc},
serde::ts_seconds,
};
use serde_derive::{Deserialize, Serialize};
use solana_sdk::{account::Account, instruction::InstructionError, pubkey::Pubkey};
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct VestState {
/// The address authorized to terminate this contract with a signed Terminate instruction
pub terminator_pubkey: Pubkey,
/// The address authorized to redeem vested tokens
pub payee_pubkey: Pubkey,
/// The day from which the vesting contract begins
#[serde(with = "ts_seconds")]
pub start_date_time: DateTime<Utc>,
/// Address of an account containing a trusted date, used to drive the vesting schedule
pub date_pubkey: Pubkey,
/// The number of lamports to send the payee if the schedule completes
pub total_lamports: u64,
/// The number of lamports the payee has already redeemed
pub redeemed_lamports: u64,
}
impl Default for VestState {
fn default() -> Self {
Self {
terminator_pubkey: Pubkey::default(),
payee_pubkey: Pubkey::default(),
start_date_time: Utc.timestamp(0, 0),
date_pubkey: Pubkey::default(),
total_lamports: 0,
redeemed_lamports: 0,
}
}
}
impl VestState {
pub fn serialize(&self, output: &mut [u8]) -> Result<(), InstructionError> {
serialize_into(output, self).map_err(|_| InstructionError::AccountDataTooSmall)
}
pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
deserialize(input).map_err(|_| InstructionError::InvalidAccountData)
}
/// Redeem vested tokens.
pub fn redeem_tokens(
&mut self,
contract_account: &mut Account,
2019-10-07 09:42:56 -07:00
current_date: Date<Utc>,
2019-10-04 14:43:50 -07:00
payee_account: &mut Account,
) {
let schedule = create_vesting_schedule(self.start_date_time.date(), self.total_lamports);
let vested_lamports = schedule
.into_iter()
.take_while(|(dt, _)| *dt <= current_date)
.map(|(_, lamports)| lamports)
.sum::<u64>();
let redeemable_lamports = vested_lamports.saturating_sub(self.redeemed_lamports);
contract_account.lamports -= redeemable_lamports;
payee_account.lamports += redeemable_lamports;
self.redeemed_lamports += redeemable_lamports;
}
/// Terminate the contract and return all tokens to the given pubkey.
pub fn terminate(&mut self, contract_account: &mut Account, payee_account: &mut Account) {
payee_account.lamports += contract_account.lamports;
contract_account.lamports = 0;
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::id;
use solana_sdk::account::Account;
#[test]
fn test_serializer() {
let mut a = Account::new(0, 512, &id());
let b = VestState::default();
b.serialize(&mut a.data).unwrap();
let c = VestState::deserialize(&a.data).unwrap();
assert_eq!(b, c);
}
#[test]
fn test_serializer_data_too_small() {
let mut a = Account::new(0, 1, &id());
let b = VestState::default();
assert_eq!(
b.serialize(&mut a.data),
Err(InstructionError::AccountDataTooSmall)
);
}
}