1use solana_sdk::pubkey::Pubkey;
7use solana_sdk::signature::Signature;
8use borsh::{BorshDeserialize, BorshSerialize};
9use serde::{Serialize, Deserialize};
10
11mod signature_serde {
13 use solana_sdk::signature::Signature;
14 use serde::{Serializer, Deserializer, Deserialize};
15 use std::str::FromStr;
16
17 pub fn serialize<S>(sig: &Signature, serializer: S) -> Result<S::Ok, S::Error>
18 where
19 S: Serializer,
20 {
21 serializer.serialize_str(&sig.to_string())
22 }
23
24 pub fn deserialize<'de, D>(deserializer: D) -> Result<Signature, D::Error>
25 where
26 D: Deserializer<'de>,
27 {
28 let s = String::deserialize(deserializer)?;
29 Signature::from_str(&s).map_err(serde::de::Error::custom)
30 }
31}
32
33#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
36pub struct SessionKey {
37 pub key: Pubkey,
39 pub scopes: Vec<PermissionScope>,
41 pub expires_at: i64,
43 pub vault: Pubkey,
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
48pub enum PermissionScope {
49 Read,
51 Capture,
53 Stake,
55}
56
57#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
59pub struct ZkProof {
60 pub proof_type: ProofType,
61 pub data: Vec<u8>,
63 pub timestamp: i64,
65 pub amount_cents: u64,
67 pub card_fingerprint: Option<String>,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
72pub enum ProofType {
73 Reclaim,
75 ZkTls,
77 Fidel,
79 SquarePos,
81 StripeConnect,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct CaptureResult {
88 #[serde(with = "signature_serde")]
90 pub signature: Signature,
91 pub cred_minted: u64,
93 pub user_amount: u64,
95 pub treasury_amount: u64,
97 pub staker_amount: u64,
99 pub merchant: Pubkey,
101}
102
103#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
105pub struct StakingPosition {
106 pub position_id: Pubkey,
107 pub amount: u64,
108 pub start_time: i64,
109 pub duration_days: u16,
110 pub unlock_time: i64,
111 pub apy_bps: u16, pub auto_compound: bool,
113 pub accrued_yield: u64,
114}
115
116#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
118pub struct YieldRecommendation {
119 pub current_apy: u16,
120 pub recommended_action: RecommendedAction,
121 pub projected_apy: u16,
122 pub reasoning: String,
123}
124
125#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
126pub enum RecommendedAction {
127 Hold,
129 Stake { amount: u64, duration_days: u16 },
131 Consolidate { from_positions: Vec<Pubkey> },
133 Extend { position: Pubkey, additional_days: u16 },
135}
136
137pub trait VaultAction {
142 fn capture_value(
149 &self,
150 session: &SessionKey,
151 merchant_id: Pubkey,
152 proof: ZkProof,
153 ) -> Result<CaptureResult, ActionError>;
154
155 fn stake_cred(
162 &self,
163 session: &SessionKey,
164 amount: u64,
165 duration_days: u16,
166 auto_compound: bool,
167 ) -> Result<StakingPosition, ActionError>;
168
169 fn claim_yield(
175 &self,
176 session: &SessionKey,
177 position_id: Option<Pubkey>, restake: bool,
179 ) -> Result<u64, ActionError>; fn request_yield_optimization(
187 &self,
188 session: &SessionKey,
189 risk_tolerance: RiskTolerance,
190 ) -> Result<YieldRecommendation, ActionError>;
191}
192
193#[derive(Debug, Clone, Copy)]
194pub enum RiskTolerance {
195 Conservative,
197 Balanced,
199 Aggressive,
201}
202
203#[derive(Debug, Clone)]
204pub enum ActionError {
205 InvalidSession,
207 InsufficientScope(PermissionScope),
209 InvalidProof(String),
211 MerchantNotFound,
213 InsufficientBalance { required: u64, available: u64 },
215 PositionLocked { unlock_time: i64 },
217 TransactionFailed(String),
219 RateLimited { retry_after_ms: u64 },
221}
222
223pub const YIELD_RATES_BPS: [(u16, u16); 4] = [
225 (30, 800), (90, 1200), (180, 1600), (365, 2000), ];
230
231pub const DISTRIBUTION: Distribution = Distribution {
233 user_bps: 8000, treasury_bps: 1400, staker_bps: 600, };
237
238pub struct Distribution {
239 pub user_bps: u16,
240 pub treasury_bps: u16,
241 pub staker_bps: u16,
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 #[test]
249 fn distribution_adds_to_100() {
250 assert_eq!(
251 DISTRIBUTION.user_bps + DISTRIBUTION.treasury_bps + DISTRIBUTION.staker_bps,
252 10000
253 );
254 }
255
256 #[test]
257 fn yield_rates_increase_with_duration() {
258 for i in 1..YIELD_RATES_BPS.len() {
259 assert!(YIELD_RATES_BPS[i].1 > YIELD_RATES_BPS[i-1].1);
260 }
261 }
262}