Skip to content

Commit

Permalink
Add Vest program
Browse files Browse the repository at this point in the history
  • Loading branch information
garious committed Sep 28, 2019
1 parent 4c5d0fc commit b797575
Show file tree
Hide file tree
Showing 8 changed files with 663 additions and 0 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Expand Up @@ -44,6 +44,7 @@ members = [
"programs/storage_program",
"programs/token_api",
"programs/token_program",
"programs/vest_api",
"programs/vote_api",
"programs/vote_program",
"replicator",
Expand Down
27 changes: 27 additions & 0 deletions programs/vest_api/Cargo.toml
@@ -0,0 +1,27 @@
[package]
name = "solana-vest-api"
version = "0.20.0-pre0"
description = "Solana Vest program API"
authors = ["Solana Maintainers <maintainers@solana.com>"]
repository = "https://github.com/solana-labs/solana"
license = "Apache-2.0"
homepage = "https://solana.com/"
edition = "2018"

[dependencies]
bincode = "1.1.4"
chrono = { version = "0.4.9", features = ["serde"] }
log = "0.4.8"
num-derive = "0.2"
num-traits = "0.2"
serde = "1.0.100"
serde_derive = "1.0.100"
solana-sdk = { path = "../../sdk", version = "0.20.0-pre0" }
solana-config-api = { path = "../config_api", version = "0.20.0-pre0" }

[dev-dependencies]
solana-runtime = { path = "../../runtime", version = "0.20.0-pre0" }

[lib]
crate-type = ["lib"]
name = "solana_budget_api"
61 changes: 61 additions & 0 deletions programs/vest_api/src/date_instruction.rs
@@ -0,0 +1,61 @@
///
/// A library for creating a trusted date oracle.
///
use bincode::{deserialize, serialized_size};
use chrono::{
prelude::{DateTime, TimeZone, Utc},
serde::ts_seconds,
};
use serde_derive::{Deserialize, Serialize};
use solana_config_api::{config_instruction, ConfigState};
use solana_sdk::{instruction::Instruction, pubkey::Pubkey};

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct DateConfig {
#[serde(with = "ts_seconds")]
pub dt: DateTime<Utc>,
}

impl Default for DateConfig {
fn default() -> Self {
Self {
dt: Utc.timestamp(0, 0),
}
}
}
impl DateConfig {
pub fn new(dt: DateTime<Utc>) -> Self {
Self { dt }
}

pub fn deserialize(input: &[u8]) -> Option<Self> {
deserialize(input).ok()
}
}

impl ConfigState for DateConfig {
fn max_space() -> u64 {
serialized_size(&Self::default()).unwrap()
}
}

/// Create a date account. The date is set to the Unix epoch.
pub fn create_account(
from_account_pubkey: &Pubkey,
date_account_pubkey: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
config_instruction::create_account::<DateConfig>(
from_account_pubkey,
date_account_pubkey,
lamports,
vec![],
)
}

/// Set the date in the date account. The account pubkey must be signed in the
/// transaction containing this instruction.
pub fn store(date_account_pubkey: &Pubkey, dt: DateTime<Utc>) -> Instruction {
let date_config = DateConfig::new(dt);
config_instruction::store(&date_account_pubkey, true, vec![], &date_config)
}
14 changes: 14 additions & 0 deletions programs/vest_api/src/lib.rs
@@ -0,0 +1,14 @@
pub mod date_instruction;
pub mod vest_instruction;
pub mod vest_processor;
pub mod vest_state;

const VEST_PROGRAM_ID: [u8; 32] = [
7, 87, 23, 47, 219, 236, 238, 33, 137, 188, 215, 141, 32, 229, 155, 195, 133, 124, 23, 232,
113, 153, 252, 252, 111, 5, 187, 128, 0, 0, 0, 0,
];

solana_sdk::solana_name_id!(
VEST_PROGRAM_ID,
"Vest111111111111111111111111111111111111111"
);
130 changes: 130 additions & 0 deletions programs/vest_api/src/vest_instruction.rs
@@ -0,0 +1,130 @@
use crate::{id, vest_state::VestState};
use bincode::serialized_size;
use chrono::prelude::{DateTime, Utc};
use num_derive::FromPrimitive;
use serde_derive::{Deserialize, Serialize};
use solana_sdk::{
instruction::{AccountMeta, Instruction},
instruction_processor_utils::DecodeError,
pubkey::Pubkey,
system_instruction,
};

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, FromPrimitive)]
pub enum VestError {
DestinationMissing,
Unauthorized,
UnexpectedProgramId,
}

impl<T> DecodeError<T> for VestError {
fn type_of() -> &'static str {
"VestError"
}
}

impl std::fmt::Display for VestError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match self {
VestError::DestinationMissing => "destination missing",
VestError::Unauthorized => "unauthorized",
VestError::UnexpectedProgramId => "unexpected program id",
}
)
}
}
impl std::error::Error for VestError {}

/// An instruction to progress the smart contract.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum VestInstruction {
/// Declare and instantiate a vesting schedule
InitializeAccount {
terminator_pubkey: Pubkey, // The address authorized to terminate this contract with a signed Terminate instruction
payee_pubkey: Pubkey, // The address authorized to redeem vested tokens
start_dt: DateTime<Utc>, // The day from which the vesting contract begins
oracle_pubkey: Pubkey, // Address of an account containing a trusted date, used to drive the vesting schedule
lamports: u64, // The number of lamports to send the payee if the schedule completes
},

/// Load an account and pass its data to the contract for inspection.
RedeemTokens,

/// Tell the contract that the `InitializeAccount` with `Signature` has been
/// signed by the containing transaction's `Pubkey`.
Terminate,
}

fn initialize_account(
terminator_pubkey: &Pubkey,
payee_pubkey: &Pubkey,
contract_pubkey: &Pubkey,
start_dt: DateTime<Utc>,
oracle_pubkey: &Pubkey,
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_dt,
oracle_pubkey: *oracle_pubkey,
lamports,
},
keys,
)
}

pub fn create_account(
terminator_pubkey: &Pubkey,
payee_pubkey: &Pubkey,
contract_pubkey: &Pubkey,
start_dt: DateTime<Utc>,
oracle_pubkey: &Pubkey,
lamports: u64,
) -> Vec<Instruction> {
let space = serialized_size(&VestState::default()).unwrap();
vec![
system_instruction::create_account(
&terminator_pubkey,
contract_pubkey,
lamports,
space,
&id(),
),
initialize_account(
terminator_pubkey,
payee_pubkey,
contract_pubkey,
start_dt,
oracle_pubkey,
lamports,
),
]
}

pub fn terminate(from: &Pubkey, contract: &Pubkey, to: &Pubkey) -> Instruction {
let mut account_metas = vec![
AccountMeta::new(*from, true),
AccountMeta::new(*contract, false),
];
if from != to {
account_metas.push(AccountMeta::new_credit_only(*to, false));
}
Instruction::new(id(), &VestInstruction::Terminate, account_metas)
}

/// Apply account data to a contract waiting on an AccountData witness.
pub fn redeem_tokens(witness_pubkey: &Pubkey, contract: &Pubkey, to: &Pubkey) -> Instruction {
let account_metas = vec![
AccountMeta::new_credit_only(*witness_pubkey, false),
AccountMeta::new(*contract, false),
AccountMeta::new_credit_only(*to, false),
];
Instruction::new(id(), &VestInstruction::RedeemTokens, account_metas)
}

0 comments on commit b797575

Please sign in to comment.