Skip to content

Commit

Permalink
Implement LQT position rewards
Browse files Browse the repository at this point in the history
  • Loading branch information
cronokirby committed Jan 31, 2025
1 parent e268d14 commit 67043e4
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 4 deletions.
54 changes: 53 additions & 1 deletion crates/core/component/dex/src/component/position_manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ use async_trait::async_trait;
use cnidarium::{EscapedByteSlice, StateRead, StateWrite};
use futures::Stream;
use futures::StreamExt;
use penumbra_sdk_asset::{asset, Balance};
use penumbra_sdk_asset::{asset, Balance, Value, STAKING_TOKEN_ASSET_ID};
use penumbra_sdk_num::Amount;
use penumbra_sdk_proto::DomainType;
use penumbra_sdk_proto::{StateReadProto, StateWriteProto};
use tap::Tap;
Expand Down Expand Up @@ -488,6 +489,57 @@ pub trait PositionManager: StateWrite + PositionRead {

Ok(reserves)
}

/// This adds extra rewards in the form of staking tokens to the reserves of a position.
#[tracing::instrument(level = "debug", skip(self))]
async fn reward_position(
&mut self,
position_id: position::Id,
reward: Amount,
) -> anyhow::Result<()> {
let prev_state = self
.position_by_id(&position_id)
.await?
.ok_or_else(|| anyhow::anyhow!("rewarding unknown position {}", position_id))?;
// The new state is the result of adding the staking token to the reserves,
// or doing nothing if for some reason this position does not have the staking token.
let new_state = {
let mut new_state = prev_state.clone();
let pair = prev_state.phi.pair;
let to_increment = if pair.asset_1() == *STAKING_TOKEN_ASSET_ID {
&mut new_state.reserves.r1
} else if pair.asset_2() == *STAKING_TOKEN_ASSET_ID {
&mut new_state.reserves.r2
} else {
tracing::error!("pair {} does not contain staking asset", pair);
return Ok(());
};
*to_increment = to_increment.checked_add(&reward).expect(&format!(
"failed to add reward {} to reserves {}",
reward, *to_increment
));
// Ok, you'd think we'd be done here, alas, the [`guard_invalid_transitions`] function
// will complain if the position has already been withdrawn, but the sequence has not yet been incremented!
new_state.state = match prev_state.state {
position::State::Opened => position::State::Opened,
position::State::Closed => position::State::Closed,
position::State::Withdrawn { sequence } => position::State::Withdrawn {
sequence: sequence.saturating_add(1),
},
};
new_state
};
self.update_position(&position_id, Some(prev_state), new_state)
.await?;
// At this point, we can credit the VCB, because the update passed.
// This is a credit because the reward has moved value *into* the DEX.
self.dex_vcb_credit(Value {
asset_id: *STAKING_TOKEN_ASSET_ID,
amount: reward,
})
.await?;
Ok(())
}
}

impl<T: StateWrite + ?Sized + Chandelier> PositionManager for T {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use async_trait::async_trait;
use cnidarium::StateWrite;
use penumbra_sdk_asset::{Value, STAKING_TOKEN_ASSET_ID};
use penumbra_sdk_community_pool::StateWriteExt as _;
use penumbra_sdk_dex::component::PositionManager as _;
use penumbra_sdk_dex::lp::position;
use penumbra_sdk_distributions::component::{StateReadExt as _, StateWriteExt as _};
use penumbra_sdk_keys::Address;
Expand Down Expand Up @@ -80,9 +81,11 @@ pub trait Bank: StateWrite + Sized {
/// Move a fraction of our issuance budget towards a position, increasing its reserves.
async fn reward_fraction_to_position(
&mut self,
_fraction: U128x128,
_lp: position::Id,
fraction: U128x128,
lp: position::Id,
) -> anyhow::Result<()> {
unimplemented!()
let reward = appropriate_budget(&mut self, fraction).await?;
self.reward_position(lp, reward).await?;
Ok(())
}
}

0 comments on commit 67043e4

Please sign in to comment.