use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature;
use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Serialize, Deserialize};
mod signature_serde {
use solana_sdk::signature::Signature;
use serde::{Serializer, Deserializer, Deserialize};
use std::str::FromStr;
pub fn serialize<S>(sig: &Signature, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&sig.to_string())
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Signature, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Signature::from_str(&s).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
pub struct SessionKey {
pub key: Pubkey,
pub scopes: Vec<PermissionScope>,
pub expires_at: i64,
pub vault: Pubkey,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub enum PermissionScope {
Read,
Capture,
Stake,
}
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
pub struct ZkProof {
pub proof_type: ProofType,
pub data: Vec<u8>,
pub timestamp: i64,
pub amount_cents: u64,
pub card_fingerprint: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, BorshSerialize, BorshDeserialize)]
pub enum ProofType {
Reclaim,
ZkTls,
Fidel,
SquarePos,
StripeConnect,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CaptureResult {
#[serde(with = "signature_serde")]
pub signature: Signature,
pub cred_minted: u64,
pub user_amount: u64,
pub treasury_amount: u64,
pub staker_amount: u64,
pub merchant: Pubkey,
}
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
pub struct StakingPosition {
pub position_id: Pubkey,
pub amount: u64,
pub start_time: i64,
pub duration_days: u16,
pub unlock_time: i64,
pub apy_bps: u16, pub auto_compound: bool,
pub accrued_yield: u64,
}
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
pub struct YieldRecommendation {
pub current_apy: u16,
pub recommended_action: RecommendedAction,
pub projected_apy: u16,
pub reasoning: String,
}
#[derive(Debug, Clone, BorshSerialize, BorshDeserialize)]
pub enum RecommendedAction {
Hold,
Stake { amount: u64, duration_days: u16 },
Consolidate { from_positions: Vec<Pubkey> },
Extend { position: Pubkey, additional_days: u16 },
}
pub trait VaultAction {
fn capture_value(
&self,
session: &SessionKey,
merchant_id: Pubkey,
proof: ZkProof,
) -> Result<CaptureResult, ActionError>;
fn stake_cred(
&self,
session: &SessionKey,
amount: u64,
duration_days: u16,
auto_compound: bool,
) -> Result<StakingPosition, ActionError>;
fn claim_yield(
&self,
session: &SessionKey,
position_id: Option<Pubkey>, restake: bool,
) -> Result<u64, ActionError>;
fn request_yield_optimization(
&self,
session: &SessionKey,
risk_tolerance: RiskTolerance,
) -> Result<YieldRecommendation, ActionError>;
}
#[derive(Debug, Clone, Copy)]
pub enum RiskTolerance {
Conservative,
Balanced,
Aggressive,
}
#[derive(Debug, Clone)]
pub enum ActionError {
InvalidSession,
InsufficientScope(PermissionScope),
InvalidProof(String),
MerchantNotFound,
InsufficientBalance { required: u64, available: u64 },
PositionLocked { unlock_time: i64 },
TransactionFailed(String),
RateLimited { retry_after_ms: u64 },
}
pub const YIELD_RATES_BPS: [(u16, u16); 4] = [
(30, 800), (90, 1200), (180, 1600), (365, 2000), ];
pub const DISTRIBUTION: Distribution = Distribution {
user_bps: 8000, treasury_bps: 1400, staker_bps: 600, };
pub struct Distribution {
pub user_bps: u16,
pub treasury_bps: u16,
pub staker_bps: u16,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn distribution_adds_to_100() {
assert_eq!(
DISTRIBUTION.user_bps + DISTRIBUTION.treasury_bps + DISTRIBUTION.staker_bps,
10000
);
}
#[test]
fn yield_rates_increase_with_duration() {
for i in 1..YIELD_RATES_BPS.len() {
assert!(YIELD_RATES_BPS[i].1 > YIELD_RATES_BPS[i-1].1);
}
}
}