penumbra_sdk_dex/
state_key.rs

1use crate::{lp::position, DirectedTradingPair, TradingPair};
2use penumbra_sdk_asset::asset;
3use std::string::String;
4
5pub mod config {
6    pub fn dex_params() -> &'static str {
7        "dex/config/dex_params"
8    }
9}
10
11pub fn value_balance(asset_id: &asset::Id) -> String {
12    format!("dex/value_balance/{asset_id}")
13}
14
15pub fn positions(trading_pair: &TradingPair, position_id: &str) -> String {
16    format!("dex/positions/{trading_pair}/opened/{position_id}")
17}
18
19/// Looks up a `Position` by its ID
20// This should only ever be called by `position_manager::Inner::update_position`.
21pub fn position_by_id(id: &position::Id) -> String {
22    format!("dex/position/{id}")
23}
24
25pub fn all_positions() -> &'static str {
26    "dex/position/"
27}
28
29pub mod candlesticks {
30
31    pub mod object {
32        pub fn block_executions() -> &'static str {
33            "dex/candlesticks/object/block_executions"
34        }
35
36        pub fn block_position_executions() -> &'static str {
37            "dex/candlesticks/object/block_position_executions"
38        }
39
40        pub fn block_swap_executions() -> &'static str {
41            "dex/candlesticks/object/block_swap_executions"
42        }
43    }
44
45    pub mod data {
46        use crate::DirectedTradingPair;
47
48        pub fn prefix() -> &'static str {
49            "dex/candlesticks/data/"
50        }
51
52        pub fn by_pair_and_height(pair: &DirectedTradingPair, height: u64) -> String {
53            format!("{}{}/{}/{height:020}", prefix(), &pair.start, &pair.end)
54        }
55
56        pub fn by_pair(pair: &DirectedTradingPair) -> String {
57            format!("{}{}/{}/", prefix(), &pair.start, &pair.end)
58        }
59    }
60}
61
62pub mod block_scoped {
63    pub mod active {
64        pub fn trading_pairs() -> &'static str {
65            "dex/block_scoped/active/trading_pairs"
66        }
67    }
68}
69
70pub fn output_data(height: u64, trading_pair: TradingPair) -> String {
71    format!(
72        "dex/output/{:020}/{}/{}",
73        height,
74        &trading_pair.asset_1(),
75        &trading_pair.asset_2()
76    )
77}
78
79pub fn swap_execution(height: u64, trading_pair: DirectedTradingPair) -> String {
80    format!(
81        "dex/swap_execution/{:020}/{}/{}",
82        height, &trading_pair.start, &trading_pair.end
83    )
84}
85
86pub fn swap_executions() -> &'static str {
87    "dex/swap_execution/"
88}
89
90pub fn arb_execution(height: u64) -> String {
91    format!("dex/arb_execution/{height:020}")
92}
93
94pub fn arb_executions() -> &'static str {
95    "dex/arb_execution/"
96}
97
98pub fn swap_flows() -> &'static str {
99    "dex/swap_flows"
100}
101
102pub fn pending_position_closures() -> &'static str {
103    "dex/pending_position_closures"
104}
105
106pub fn recently_accessed_assets() -> &'static str {
107    "dex/recently_accessed_assets"
108}
109
110pub fn pending_payloads() -> &'static str {
111    "dex/pending_payloads"
112}
113
114pub fn pending_outputs() -> &'static str {
115    "dex/pending_outputs"
116}
117
118pub fn aggregate_value() -> &'static str {
119    "dex/aggregate_value"
120}
121
122pub(crate) mod engine {
123    use super::*;
124    use crate::lp::BareTradingFunction;
125
126    pub(crate) mod counter {
127        pub(crate) mod num_positions {
128            use crate::TradingPair;
129
130            pub(crate) fn prefix() -> &'static str {
131                "dex/internal/counter/num_positions/"
132            }
133
134            pub(crate) fn by_trading_pair(trading_pair: &TradingPair) -> [u8; 99] {
135                let mut key = [0u8; 99];
136                let prefix_bytes = prefix().as_bytes();
137                let canonical_pair_bytes = trading_pair.to_bytes();
138
139                key[0..35].copy_from_slice(prefix_bytes);
140                key[35..99].copy_from_slice(&canonical_pair_bytes);
141                key
142            }
143        }
144    }
145
146    pub(crate) mod routable_assets {
147        use penumbra_sdk_asset::asset;
148        use penumbra_sdk_num::Amount;
149
150        use super::*;
151
152        // An ordered encoding of every asset `B` routable from `A` based on the
153        // aggregate liquidity available to route from `B` to `A` (aka. the base liquidity).
154        //
155        /// # Encoding
156        /// The prefix key is encoded as `domain || A`.
157        pub(crate) fn starting_from(from: &asset::Id) -> [u8; 39] {
158            let mut key = [0u8; 39];
159            key[0..7].copy_from_slice(b"dex/ra/");
160            key[7..39].copy_from_slice(&from.to_bytes());
161            key
162        }
163
164        /// A record that an asset `A` is routable to an asset `B` and contains the
165        /// aggregate liquidity available to route from `B` to `A` (aka. the base liquidity).
166        ///
167        /// # Encoding
168        /// The full key is encoded as: `prefix || BE(aggregate_base_liquidity)`
169        pub(crate) fn key(from: &asset::Id, a_from_b: Amount) -> [u8; 55] {
170            let mut key = [0u8; 55];
171            key[0..7].copy_from_slice(b"dex/ra/");
172            key[7..32 + 7].copy_from_slice(&from.to_bytes());
173            // Use the complement of the amount to ensure that the keys are ordered in descending order.
174            key[32 + 7..32 + 7 + 16].copy_from_slice(&(!a_from_b).to_be_bytes());
175            key
176        }
177
178        /// A lookup index used to reconstruct and update the primary index entries.
179        /// It maps a directed trading pair `A -> B` to the aggregate liquidity available
180        /// to route from `B` to `A` (aka. the base asset liquidity).
181        ///
182        /// # Encoding
183        /// The lookup key is encoded as `prefix_lookup || start_asset|| end_asset`.
184        pub(crate) fn lookup_base_liquidity_by_pair(pair: &DirectedTradingPair) -> [u8; 71] {
185            let mut key = [0u8; 71];
186            key[0..7].copy_from_slice(b"dex/ab/");
187            key[7..39].copy_from_slice(&pair.start.to_bytes());
188            key[39..71].copy_from_slice(&pair.end.to_bytes());
189            key
190        }
191    }
192
193    pub(crate) mod price_index {
194
195        use super::*;
196
197        pub(crate) fn prefix(pair: &DirectedTradingPair) -> [u8; 71] {
198            let mut key = [0u8; 71];
199            key[0..7].copy_from_slice(b"dex/pi/");
200            key[7..39].copy_from_slice(&pair.start.to_bytes());
201            key[39..71].copy_from_slice(&pair.end.to_bytes());
202            key
203        }
204
205        pub(crate) fn key(
206            pair: &DirectedTradingPair,
207            btf: &BareTradingFunction,
208            id: &position::Id,
209        ) -> Vec<u8> {
210            let id_bytes = id.0;
211            let mut key = [0u8; 135];
212            key[0..71].copy_from_slice(&prefix(pair));
213            key[71..103].copy_from_slice(&btf.effective_price_key_bytes());
214            key[103..135].copy_from_slice(&id_bytes);
215            key.to_vec()
216        }
217    }
218}
219
220pub(crate) mod eviction_queue {
221    pub(crate) mod inventory_index {
222        use crate::lp::position;
223        use crate::DirectedTradingPair;
224        use anyhow::ensure;
225        use penumbra_sdk_num::Amount;
226
227        pub(crate) fn by_trading_pair(pair: &DirectedTradingPair) -> [u8; 107] {
228            let mut prefix = [0u8; 107];
229            prefix[0..43].copy_from_slice(b"dex/internal/eviction_queue/inventory_index");
230            prefix[43..75].copy_from_slice(&pair.start.to_bytes());
231            prefix[75..107].copy_from_slice(&pair.end.to_bytes());
232            prefix
233        }
234
235        pub(crate) fn key(
236            pair: &DirectedTradingPair,
237            inventory: Amount,
238            id: &position::Id,
239        ) -> [u8; 155] {
240            let mut full_key = [0u8; 155];
241            let prefix = by_trading_pair(pair);
242            full_key[0..107].copy_from_slice(&prefix);
243            full_key[107..123].copy_from_slice(&inventory.to_be_bytes());
244            full_key[123..155].copy_from_slice(&id.0);
245
246            full_key
247        }
248
249        pub(crate) fn parse_id_from_key(key: Vec<u8>) -> anyhow::Result<[u8; 32]> {
250            ensure!(key.len() == 155, "key must be 155 bytes");
251            let k = &key[123..155];
252            Ok(k.try_into()?)
253        }
254    }
255}