1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
use std::convert::TryFrom;

use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use serde::{Deserialize, Serialize};

use solana_sdk::{clock::UnixTimestamp, pubkey::Pubkey};

solana_sdk::declare_id!("VAcccHVjpknkW5N5R9sfRppQxYJrJYVV7QJGKchkQj5");

/// A wrapper enum for consistency across programs
#[derive(Debug, PartialEq)]
#[derive(BorshSerialize, BorshDeserialize, BorshSchema)]
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum VelasAccountType {
    Account(VAccountInfo),
    Storage(VAccountStorage),
}

/// Program states.
#[repr(C)]
#[derive(PartialEq, Debug, Clone)]
#[derive(BorshSerialize, BorshDeserialize, BorshSchema)]
#[derive(Serialize, Deserialize)]
pub struct VAccountInfo {
    /// Vaccount version
    pub version: u8,
    /// Genegis owner key that generate Vaccount address
    pub genesis_seed_key: Pubkey,
    /// Storage version
    pub storage_version: u16,
    /// Storage address
    pub storage: Pubkey,
}

/// Storage of the basic Vaccount information.
#[repr(C)]
#[derive(PartialEq, Debug, Clone)]
#[derive(BorshSerialize, BorshDeserialize, BorshSchema)]
#[derive(Serialize, Deserialize)]
pub struct VAccountStorage {
    /// Owner key in not extended VAccount
    pub owners: Vec<Pubkey>,
    /// Operational in not extended VAccount
    pub operationals: Vec<Operational>,
}

/// Operational key state.
#[repr(C)]
#[derive(Clone, Debug, Default, PartialEq)]
#[derive(BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Serialize, Deserialize)]
pub struct Operational {
    /// Operational key
    pub pubkey: Pubkey,
    /// Operational key state
    pub state: OperationalState,
    /// Type of the agent session associated with an operational key
    pub agent_type: Vec<u8>,
    /// Allowed instruction for operational key
    pub scopes: Vec<u8>,
    /// Allowed programs to call
    pub whitelist_programs: Vec<ExternalProgram>,
    /// Allowed token accounts
    pub whitelist_tokens: Vec<ExternalToken>,
    /// Master key is allowed to call any instruction in Vaccount
    pub is_master_key: bool,
}

/// Operational key state.
#[repr(C)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[derive(BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Serialize, Deserialize)]
pub struct ExternalProgram {
    /// Allowed to call program code id
    pub program_id: Pubkey,
    /// Allowed to call instruction inside program
    pub scopes: Vec<u8>,
}

/// Operational key state.
#[repr(u8)]
#[derive(Clone, Copy, Debug, PartialEq)]
#[derive(BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Serialize, Deserialize)]
pub enum OperationalState {
    /// Operational key is initialized
    Initialized,
    /// Operational has been frozen by the owner/operational freeze authority.
    Frozen,
}

/// Token account.
#[repr(C)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[derive(BorshSchema, BorshSerialize, BorshDeserialize)]
#[derive(Serialize, Deserialize)]
pub struct ExternalToken {
    /// Token account with daily transfer limit
    pub account: TokenAccount,
    /// Last uses of transfer
    pub last_transfer: UnixTimestamp,
}

/// Token daily limit.
#[repr(C)]
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
#[derive(BorshSchema, BorshSerialize, BorshDeserialize)]
#[derive(Serialize, Deserialize)]
pub struct TokenAccount {
    /// Token address with vaccount authority
    pub token_account: Pubkey,
    /// The remainder of the daily limit lamports for transfer
    pub remainder_daily_limit: u64,
}

impl Default for OperationalState {
    fn default() -> Self {
        OperationalState::Initialized
    }
}

// TODO: try to avoid direct size hardcodes
pub const ACCOUNT_LEN: usize = 67;

#[derive(Debug)]
pub enum ParseError {
    AccountNotParsable,
}

impl TryFrom<&[u8]> for VelasAccountType {
    type Error = ParseError;

    fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
        if data.len() == ACCOUNT_LEN {
            VAccountInfo::try_from_slice(data).map(VelasAccountType::Account)
        } else {
            VAccountStorage::try_from_slice(data).map(VelasAccountType::Storage)
        }
        .map_err(|_| ParseError::AccountNotParsable)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[ignore] // TODO
    fn it_checks_account_len() {
        assert_eq!(std::mem::size_of::<VAccountInfo>(), ACCOUNT_LEN);
    }
}