Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement parameters for liquidity tournament control (in funding) #5020

Merged
merged 4 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions crates/core/app/src/params/change.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ impl AppParameters {
fixed_gas_prices: _,
fixed_alt_gas_prices: _,
},
funding_params: FundingParameters {},
funding_params:
FundingParameters {
liquidity_tournament: _,
},
governance_params:
GovernanceParameters {
proposal_voting_blocks: _,
Expand Down Expand Up @@ -171,7 +174,10 @@ impl AppParameters {
fixed_gas_prices: _,
fixed_alt_gas_prices: _,
},
funding_params: FundingParameters {},
funding_params:
FundingParameters {
liquidity_tournament: _,
},
governance_params:
GovernanceParameters {
proposal_voting_blocks,
Expand Down
2 changes: 1 addition & 1 deletion crates/core/component/funding/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ impl Component for Funding {
#[instrument(name = "funding", skip(state, app_state))]
async fn init_chain<S: StateWrite>(mut state: S, app_state: Option<&Self::AppState>) {
match app_state {
None => { /* Checkpoint -- no-op */ }
None => { /* no-op */ }
Some(genesis) => {
state.put_funding_params(genesis.funding_params.clone());
}
Expand Down
73 changes: 67 additions & 6 deletions crates/core/component/funding/src/params.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,60 @@
use penumbra_sdk_num::Percentage;
use penumbra_sdk_proto::core::component::funding::v1 as pb;
use penumbra_sdk_proto::DomainType;
use serde::{Deserialize, Serialize};

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct LiquidityTournamentParameters {
// The fraction of gauge votes that an asset must clear to receive rewards.
pub gauge_threshold: Percentage,
// The maximum number of liquidity positions that can receive rewards.
pub max_positions: u64,
// The maximum number of delegators that can receive rewards.
pub max_delegators: u64,
// The share of rewards that go to delegators, instead of positions.
pub delegator_share: Percentage,
}

impl Default for LiquidityTournamentParameters {
fn default() -> Self {
Self {
gauge_threshold: Percentage::from_percent(100),
max_positions: 0,
max_delegators: 0,
delegator_share: Percentage::zero(),
}
}
}

impl TryFrom<pb::funding_parameters::LiquidityTournament> for LiquidityTournamentParameters {
type Error = anyhow::Error;

fn try_from(proto: pb::funding_parameters::LiquidityTournament) -> Result<Self, Self::Error> {
Ok(Self {
gauge_threshold: Percentage::from_percent(proto.gauge_threshold_percent),
max_positions: proto.max_positions,
max_delegators: proto.max_delegators,
delegator_share: Percentage::from_percent(proto.delegator_share_percent),
})
}
}

impl From<LiquidityTournamentParameters> for pb::funding_parameters::LiquidityTournament {
fn from(value: LiquidityTournamentParameters) -> Self {
Self {
gauge_threshold_percent: value.gauge_threshold.to_percent(),
max_positions: value.max_positions,
max_delegators: value.max_delegators,
delegator_share_percent: value.delegator_share.to_percent(),
}
}
}

#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(try_from = "pb::FundingParameters", into = "pb::FundingParameters")]
pub struct FundingParameters {}
pub struct FundingParameters {
pub liquidity_tournament: LiquidityTournamentParameters,
}

impl DomainType for FundingParameters {
type Proto = pb::FundingParameters;
Expand All @@ -13,19 +63,30 @@ impl DomainType for FundingParameters {
impl TryFrom<pb::FundingParameters> for FundingParameters {
type Error = anyhow::Error;

fn try_from(_params: pb::FundingParameters) -> anyhow::Result<Self> {
Ok(FundingParameters {})
fn try_from(proto: pb::FundingParameters) -> anyhow::Result<Self> {
Ok(FundingParameters {
// Explicitly consider missing parameters to *be* the default parameters, for upgrades.
liquidity_tournament: proto
.liquidity_tournament
.map(LiquidityTournamentParameters::try_from)
.transpose()?
.unwrap_or_default(),
})
}
}

impl From<FundingParameters> for pb::FundingParameters {
fn from(_params: FundingParameters) -> Self {
pb::FundingParameters {}
fn from(params: FundingParameters) -> Self {
pb::FundingParameters {
liquidity_tournament: Some(params.liquidity_tournament.into()),
}
}
}

impl Default for FundingParameters {
fn default() -> Self {
Self {}
Self {
liquidity_tournament: LiquidityTournamentParameters::default(),
}
}
}
2 changes: 2 additions & 0 deletions crates/core/num/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
mod amount;
pub mod fixpoint;
mod percentage;

pub use amount::{Amount, AmountVar};
pub use percentage::Percentage;
36 changes: 36 additions & 0 deletions crates/core/num/src/percentage/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/// Represents a percentage value.
///
/// Useful for more robust typesafety, versus just passing around a `u64` which
/// is merely *understood* to only contain values in [0, 100].
///
/// Defaults to 0%.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Percentage(u64);

impl Percentage {
/// 0%
pub const fn zero() -> Self {
Self(0)
}

/// Convert this value into a `u64` in [0, 100];
pub const fn to_percent(self) -> u64 {
self.0
}

/// Given an arbitrary `u64`, produce a percentage, *saturating* at 100.
pub fn from_percent(p: u64) -> Self {
Self(u64::min(p.into(), 100))
}
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_percentage_operations() {
assert_eq!(Percentage::from_percent(101), Percentage::from_percent(100));
assert_eq!(Percentage::from_percent(48).to_percent(), 48);
}
}
46 changes: 45 additions & 1 deletion crates/proto/src/gen/penumbra.core.component.funding.v1.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,51 @@
// This file is @generated by prost-build.
/// Funding component configuration data.
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
pub struct FundingParameters {}
pub struct FundingParameters {
/// The parameters governing the funding of the liquidity tournament.
#[prost(message, optional, tag = "1")]
pub liquidity_tournament: ::core::option::Option<
funding_parameters::LiquidityTournament,
>,
}
/// Nested message and enum types in `FundingParameters`.
pub mod funding_parameters {
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
pub struct LiquidityTournament {
/// The fraction of gauge votes that an asset must pass to get any rewards.
///
/// Takes a value in \[0, 100\].
#[prost(uint64, tag = "1")]
pub gauge_threshold_percent: u64,
/// The maximum number of liquidity positions that can receive rewards.
///
/// This avoids potential DoS vectors with processing a large number of small positions.
#[prost(uint64, tag = "2")]
pub max_positions: u64,
/// The maximum number of delegators that can be rewarded.
///
/// Also avoids potential DoS vectors
#[prost(uint64, tag = "3")]
pub max_delegators: u64,
/// The share of rewards which will go to delegators, opposed with positions.
///
/// Takes a value in \[0, 100\].
#[prost(uint64, tag = "4")]
pub delegator_share_percent: u64,
}
impl ::prost::Name for LiquidityTournament {
const NAME: &'static str = "LiquidityTournament";
const PACKAGE: &'static str = "penumbra.core.component.funding.v1";
fn full_name() -> ::prost::alloc::string::String {
"penumbra.core.component.funding.v1.FundingParameters.LiquidityTournament"
.into()
}
fn type_url() -> ::prost::alloc::string::String {
"/penumbra.core.component.funding.v1.FundingParameters.LiquidityTournament"
.into()
}
}
}
impl ::prost::Name for FundingParameters {
const NAME: &'static str = "FundingParameters";
const PACKAGE: &'static str = "penumbra.core.component.funding.v1";
Expand Down
Loading