diff --git a/crates/core/component/dex/src/state_key.rs b/crates/core/component/dex/src/state_key.rs index 6ce4e5ccb6..b920bea4ed 100644 --- a/crates/core/component/dex/src/state_key.rs +++ b/crates/core/component/dex/src/state_key.rs @@ -119,6 +119,82 @@ pub fn aggregate_value() -> &'static str { "dex/aggregate_value" } +pub mod lqt { + pub mod v1 { + pub mod lp { + pub mod lookup { + use penumbra_sdk_asset::asset; + + pub(crate) fn _prefix(epoch_index: u64) -> String { + format!("dex/lqt/v1/lp/lookup/{epoch_index:020}/") + } + + // A lookup index used to update cumulative volumes. + /// It maps an trading pair (staking token, asset) to the cumulative volume of outbound liquidity. + /// + /// # Key Encoding + /// The lookup key is encoded as `prefix || asset` + /// # Value Encoding + /// The value is encoded as `BE(Amount)` + pub(crate) fn _volume_by_pair(epoch_index: u64, asset: asset::Id) -> [u8; 74] { + let prefix_bytes = _prefix(epoch_index); + let mut key = [0u8; 74]; + key[0..42].copy_from_slice(prefix_bytes.as_bytes()); + key[42..42 + 32].copy_from_slice(&asset.to_bytes()); + key + } + } + + pub mod by_volume { + use anyhow::{ensure, Result}; + use penumbra_sdk_asset::asset; + use penumbra_sdk_num::Amount; + + pub fn prefix(epoch_index: u64) -> String { + format!("dex/lqt/v1/lp/by_volume/{epoch_index:020}/") + } + + /// Tracks the cumulative volume of outbound liquidity for a given pair. + /// The pair is always connected by the staking token, which is the implicit numeraire. + /// + /// # Encoding + /// The full key is encoded as: `prefix || asset || BE(volume)` + pub(crate) fn _key( + epoch_index: u64, + asset: &asset::Id, + volume: Amount, + ) -> [u8; 93] { + let prefix_bytes = prefix(epoch_index); + let mut key = [0u8; 93]; + key[0..45].copy_from_slice(prefix_bytes.as_bytes()); + key[45..45 + 32].copy_from_slice(&asset.to_bytes()); + key[45 + 32..45 + 32 + 16].copy_from_slice(&(!volume).to_be_bytes()); + key + } + + /// Extract the cumulative amount of liquidity from a fully specified key. + /// + /// # Errors + /// This function will return an error if the key is not 72 bytes. Or, if the + /// key contains an invalid asset identifier. + pub(crate) fn _parse_key(key: &[u8]) -> Result<(asset::Id, Amount)> { + ensure!(key.len() == 93, "key must be 93 bytes"); + + // skip the first 45 bytes of prefix + let raw_asset: [u8; 32] = key[45..45 + 32].try_into()?; + let asset: asset::Id = raw_asset.try_into()?; + + let raw_amount: [u8; 16] = key[45 + 32..45 + 32 + 16].try_into()?; + let amount_complement = Amount::from_be_bytes(raw_amount); + let amount = !amount_complement; + + Ok((asset, amount)) + } + } + } + } +} + pub(crate) mod engine { use super::*; use crate::lp::BareTradingFunction;