exponent_amm_exchange_rate/
lib.rs1use borsh::{BorshDeserialize, BorshSerialize};
2use solana_program::{account_info::AccountInfo, pubkey::Pubkey};
3use std::str::FromStr;
4
5#[derive(BorshSerialize, BorshDeserialize, Clone, Default)]
7pub struct MarketFinancials {
8 pub expiration_ts: u64,
10
11 pub pt_balance: u64,
14
15 pub sy_balance: u64,
18
19 pub ln_fee_rate_root: f64,
21
22 pub last_ln_implied_rate: f64,
25
26 pub rate_scalar_root: f64,
28}
29
30impl MarketFinancials {
31 pub const SIZE_OF: usize = 8 + 8 + 8 + 16 + 16 + 16;
32
33 pub fn exchange_rate(&self, unix_timestamp: u64) -> f64 {
34 exponent_time_curve::math::exchange_rate_from_ln_implied_rate::<f64>(
35 self.last_ln_implied_rate.into(),
36 self.sec_remaining(unix_timestamp),
37 )
38 }
39
40 fn sec_remaining(&self, now: u64) -> u64 {
41 if now > self.expiration_ts {
42 0
43 } else {
44 self.expiration_ts - now
45 }
46 }
47}
48
49pub struct GetExchangeRateResult {
50 pub pt_asset_exchange_rate: f64,
51}
52
53pub fn get_exchange_rate(
54 market_account_info: &AccountInfo,
55 vault_account_info: &AccountInfo,
56 current_unix_timestamp: u64,
57) -> GetExchangeRateResult {
58 let program_id = Pubkey::from_str("ExponentnaRg3CQbW6dqQNZKXp7gtZ9DGMp1cwC4HAS7").unwrap();
59 assert_eq!(
60 market_account_info.owner, &program_id,
61 "Market account not owned by program"
62 );
63 assert_eq!(
64 vault_account_info.owner, &program_id,
65 "Vault account not owned by program"
66 );
67
68 let market_data = market_account_info.try_borrow_data().unwrap();
69 let vault_data = vault_account_info.try_borrow_data().unwrap();
70
71 assert_eq!(
72 &market_data[0..8],
73 &[212, 4, 132, 126, 169, 121, 121, 20],
74 "Invalid market account discriminator"
75 );
76 assert_eq!(
77 &vault_data[0..8],
78 &[211, 8, 232, 43, 2, 152, 117, 119],
79 "Invalid vault account discriminator"
80 );
81
82 let financials =
83 MarketFinancials::deserialize(&mut &market_data[364..364 + MarketFinancials::SIZE_OF])
84 .unwrap();
85
86 let exchange_rate_from_ln_implied_rate = financials.exchange_rate(current_unix_timestamp);
87
88 GetExchangeRateResult {
89 pt_asset_exchange_rate: 1.00 / exchange_rate_from_ln_implied_rate,
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 #[cfg(not(target_os = "solana"))]
97 use solana_client::rpc_client::RpcClient;
98
99 #[test]
100 #[cfg(not(target_os = "solana"))]
101 fn test_exchange_rates_with_real_data() {
102 let rpc_url = "https://api.mainnet-beta.solana.com".to_string();
104 let client = RpcClient::new(rpc_url);
105
106 let vault_address =
108 Pubkey::from_str("9YbaicMsXrtupkpD72pdWBfU6R7EJfSByw75sEpDM1uH").unwrap();
109
110 let program_id = Pubkey::from_str("ExponentnaRg3CQbW6dqQNZKXp7gtZ9DGMp1cwC4HAS7").unwrap();
112 let (market_address, _) =
113 Pubkey::find_program_address(&[b"market", vault_address.as_ref()], &program_id);
114
115 let slot = client.get_slot().expect("Failed to get slot");
117 let current_time = client
118 .get_block_time(slot)
119 .expect("Failed to get block time") as u64;
120
121 let mut vault_account = client
123 .get_account(&vault_address)
124 .expect("Failed to get vault account");
125 let mut market_account = client
126 .get_account(&market_address)
127 .expect("Failed to get market account");
128
129 let mut vault_lamports = vault_account.lamports;
131 let mut market_lamports = market_account.lamports;
132
133 let vault_account_info = AccountInfo::new(
134 &vault_address,
135 false,
136 false,
137 &mut vault_lamports,
138 &mut vault_account.data,
139 &program_id,
140 false,
141 0,
142 );
143
144 let market_account_info = AccountInfo::new(
145 &market_address,
146 false,
147 false,
148 &mut market_lamports,
149 &mut market_account.data,
150 &program_id,
151 false,
152 0,
153 );
154
155 let result = get_exchange_rate(&market_account_info, &vault_account_info, current_time);
157
158 println!("PT -> Asset rate: {}", result.pt_asset_exchange_rate);
159 }
160}