1use core::fmt;
2
3use redis::ToRedisArgs;
4use solana_pubkey::Pubkey;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub struct RedisKey(String);
9
10impl RedisKey {
11 pub fn from_string(s: String) -> Self {
13 Self(s)
14 }
15
16 pub fn as_str(&self) -> &str {
18 &self.0
19 }
20
21 pub const VAULT_PREFIX: &'static str = "account:pyra:vault";
24 pub const WITHDRAW_ORDER_PREFIX: &'static str = "account:pyra:withdraw_order";
25 pub const SPEND_LIMITS_ORDER_PREFIX: &'static str = "account:pyra:spend_limits_order";
26 pub const DEPOSIT_ADDRESS_PREFIX: &'static str = "account:pyra:deposit_address";
27 pub const DEPOSIT_ADDRESS_OWNER_PREFIX: &'static str = "reverse:deposit_address";
28 pub const USER_WITHDRAW_ORDERS_PREFIX: &'static str = "user:orders:withdraws";
29 pub const USER_SPEND_LIMITS_ORDERS_PREFIX: &'static str = "user:orders:spend_limits";
30 pub const TOKEN_ACCOUNT_PREFIX: &'static str = "account:token";
31 pub const PRICE_PREFIX: &'static str = "price";
32 pub const ORACLE_PREFIX: &'static str = "oracle";
33 pub const ORACLE_CACHE_PREFIX: &'static str = "account:oracle";
34 pub const REGISTERED_WALLETS: &'static str = "registered_wallets";
35 pub const REGISTERED_WALLETS_CHANGES: &'static str = "registered_wallets:changes";
36 pub const USER_SOLANA_ADDRESS_PREFIX: &'static str = "user:solana_address";
37 pub const USER_PROVIDER_PREFIX: &'static str = "user:provider";
38 pub const PENDING_DEPOSIT_PREFIX: &'static str = "pending_deposits";
39
40 pub fn pattern(prefix: &str) -> String {
44 format!("{prefix}:*")
45 }
46
47 pub fn vault(vault: &Pubkey) -> Self {
50 let p = Self::VAULT_PREFIX;
51 Self(format!("{p}:{vault}"))
52 }
53
54 pub fn deposit_address_spl(deposit_address: &Pubkey, mint: &Pubkey) -> Self {
55 let p = Self::DEPOSIT_ADDRESS_PREFIX;
56 Self(format!("{p}:{deposit_address}:{mint}"))
57 }
58
59 pub fn deposit_address_sol(deposit_address: &Pubkey) -> Self {
60 let p = Self::DEPOSIT_ADDRESS_PREFIX;
61 Self(format!("{p}:{deposit_address}:native"))
62 }
63
64 pub fn deposit_address_owner(deposit_address: &Pubkey) -> Self {
66 let p = Self::DEPOSIT_ADDRESS_OWNER_PREFIX;
67 Self(format!("{p}:{deposit_address}"))
68 }
69
70 pub fn withdraw_order(order: &Pubkey) -> Self {
71 let p = Self::WITHDRAW_ORDER_PREFIX;
72 Self(format!("{p}:{order}"))
73 }
74
75 pub fn user_withdraw_orders(owner: &Pubkey) -> Self {
76 let p = Self::USER_WITHDRAW_ORDERS_PREFIX;
77 Self(format!("{p}:{owner}"))
78 }
79
80 pub fn spend_limits_order(order: &Pubkey) -> Self {
81 let p = Self::SPEND_LIMITS_ORDER_PREFIX;
82 Self(format!("{p}:{order}"))
83 }
84
85 pub fn user_spend_limits_orders(owner: &Pubkey) -> Self {
86 let p = Self::USER_SPEND_LIMITS_ORDERS_PREFIX;
87 Self(format!("{p}:{owner}"))
88 }
89
90 pub fn token_account(token_account: &Pubkey) -> Self {
91 let p = Self::TOKEN_ACCOUNT_PREFIX;
92 Self(format!("{p}:{token_account}"))
93 }
94
95 pub fn price(asset_id: pyra_tokens::AssetId) -> Self {
98 let p = Self::PRICE_PREFIX;
99 Self(format!("{p}:{asset_id}"))
100 }
101
102 pub fn oracle(oracle: &Pubkey) -> Self {
104 let p = Self::ORACLE_PREFIX;
105 Self(format!("{p}:{oracle}"))
106 }
107
108 pub fn oracle_cache(asset_id: pyra_tokens::AssetId) -> Self {
110 let p = Self::ORACLE_CACHE_PREFIX;
111 Self(format!("{p}:{asset_id}"))
112 }
113
114 pub fn registered_wallets() -> Self {
117 Self(Self::REGISTERED_WALLETS.to_string())
118 }
119
120 pub fn user_solana_address(owner: &str) -> Self {
122 let p = Self::USER_SOLANA_ADDRESS_PREFIX;
123 Self(format!("{p}:{owner}"))
124 }
125
126 pub fn user_provider(user_id: &str) -> Self {
128 let p = Self::USER_PROVIDER_PREFIX;
129 Self(format!("{p}:{user_id}"))
130 }
131
132 pub fn pending_deposit(owner: &str, mint: &str) -> Self {
134 let p = Self::PENDING_DEPOSIT_PREFIX;
135 Self(format!("{p}:{owner}:{mint}"))
136 }
137}
138
139impl fmt::Display for RedisKey {
142 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143 f.write_str(&self.0)
144 }
145}
146
147impl ToRedisArgs for RedisKey {
148 fn write_redis_args<W: ?Sized + redis::RedisWrite>(&self, out: &mut W) {
149 out.write_arg(self.0.as_bytes());
150 }
151}
152
153impl AsRef<str> for RedisKey {
154 fn as_ref(&self) -> &str {
155 &self.0
156 }
157}
158
159#[cfg(test)]
160#[allow(
161 clippy::allow_attributes,
162 clippy::allow_attributes_without_reason,
163 clippy::unwrap_used,
164 clippy::expect_used,
165 clippy::panic,
166 clippy::arithmetic_side_effects
167)]
168mod tests {
169 use super::*;
170 use pyra_tokens::AssetId;
171 use std::str::FromStr;
172
173 #[test]
174 fn vault_key_format() {
175 let pk = Pubkey::from_str("11111111111111111111111111111111").unwrap();
176 let key = RedisKey::vault(&pk);
177 assert_eq!(
178 key.to_string(),
179 "account:pyra:vault:11111111111111111111111111111111"
180 );
181 }
182
183 #[test]
184 fn deposit_address_spl_key_format() {
185 let addr = Pubkey::from_str("11111111111111111111111111111111").unwrap();
186 let mint = Pubkey::from_str("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v").unwrap();
187 let key = RedisKey::deposit_address_spl(&addr, &mint);
188 assert!(key.to_string().starts_with("account:pyra:deposit_address:"));
189 assert!(key
190 .to_string()
191 .contains("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"));
192 }
193
194 #[test]
195 fn deposit_address_sol_key_format() {
196 let addr = Pubkey::from_str("11111111111111111111111111111111").unwrap();
197 let key = RedisKey::deposit_address_sol(&addr);
198 assert!(key.to_string().ends_with(":native"));
199 }
200
201 #[test]
202 fn price_key_format() {
203 let key = RedisKey::price(AssetId::USDC);
204 assert_eq!(key.to_string(), "price:0");
205 }
206
207 #[test]
208 fn price_key_uses_asset_id() {
209 let key = RedisKey::price(AssetId::PYUSD);
211 assert_eq!(key.to_string(), "price:6");
212 }
213
214 #[test]
215 fn oracle_cache_key_uses_asset_id() {
216 let key = RedisKey::oracle_cache(AssetId::PYUSD);
218 assert_eq!(key.to_string(), "account:oracle:6");
219 }
220
221 #[test]
222 fn pattern_format() {
223 let pat = RedisKey::pattern(RedisKey::VAULT_PREFIX);
224 assert_eq!(pat, "account:pyra:vault:*");
225 }
226
227 #[test]
228 fn user_keys_format() {
229 let key = RedisKey::user_solana_address("abc123");
230 assert_eq!(key.to_string(), "user:solana_address:abc123");
231
232 let key = RedisKey::user_provider("did:privy:abc");
233 assert_eq!(key.to_string(), "user:provider:did:privy:abc");
234 }
235
236 #[test]
237 fn pending_deposit_format() {
238 let key = RedisKey::pending_deposit("owner1", "mint1");
239 assert_eq!(key.to_string(), "pending_deposits:owner1:mint1");
240 }
241
242 #[test]
243 fn redis_key_as_ref() {
244 let key = RedisKey::price(AssetId::WSOL);
245 let s: &str = key.as_ref();
246 assert_eq!(s, "price:1");
247 }
248}