penumbra_sdk_auction/component/
auction.rs1use crate::component::dutch_auction::HandleDutchTriggers;
2use crate::event;
3use anyhow::Result;
4use async_trait::async_trait;
5use cnidarium::{StateRead, StateWrite};
6use cnidarium_component::Component;
7use penumbra_sdk_asset::asset;
8use penumbra_sdk_asset::Value;
9use penumbra_sdk_num::Amount;
10use penumbra_sdk_proto::StateReadProto;
11use penumbra_sdk_proto::StateWriteProto;
12use std::sync::Arc;
13use tap::Tap;
14use tendermint::v0_37::abci;
15use tracing::instrument;
16
17use crate::{params::AuctionParameters, state_key};
18
19pub struct Auction {}
20
21#[async_trait]
22impl Component for Auction {
23 type AppState = crate::genesis::Content;
24
25 #[instrument(name = "auction", skip(state, app_state))]
26 async fn init_chain<S: StateWrite>(mut state: S, app_state: Option<&Self::AppState>) {
27 match app_state {
28 None => { }
29 Some(content) => {
30 state.put_auction_params(content.auction_params.clone());
31 }
32 }
33 }
34
35 #[instrument(name = "auction", skip(_state, _begin_block))]
36 async fn begin_block<S: StateWrite + 'static>(
37 _state: &mut Arc<S>,
38 _begin_block: &abci::request::BeginBlock,
39 ) {
40 }
41
42 #[instrument(name = "auction", skip(state, end_block))]
43 async fn end_block<S: StateWrite + 'static>(
44 state: &mut Arc<S>,
45 end_block: &abci::request::EndBlock,
46 ) {
47 let state: &mut S = Arc::get_mut(state).expect("state should be unique");
48 let _ = state.process_triggers(end_block.height as u64).await;
49 }
50
51 #[instrument(name = "auction", skip(_state))]
52 async fn end_epoch<S: StateWrite + 'static>(_state: &mut Arc<S>) -> Result<()> {
53 Ok(())
54 }
55}
56
57#[async_trait]
59pub trait StateReadExt: StateRead {
60 async fn get_auction_params(&self) -> Result<AuctionParameters> {
61 self.get(state_key::parameters::key())
62 .await
63 .expect("no deserialization errors")
64 .ok_or_else(|| anyhow::anyhow!("Missing AuctionParameters"))
65 }
66
67 fn auction_params_updated(&self) -> bool {
68 self.object_get::<()>(state_key::parameters::updated_flag())
69 .is_some()
70 }
71
72 #[instrument(skip(self))]
75 async fn get_auction_value_balance_for(&self, asset_id: &asset::Id) -> Amount {
76 self.get(&state_key::value_balance::for_asset(asset_id))
77 .await
78 .expect("failed to fetch auction value breaker balance")
79 .unwrap_or_else(Amount::zero)
80 .tap(|vcb| tracing::trace!(?vcb))
81 }
82}
83
84impl<T: StateRead + ?Sized> StateReadExt for T {}
85
86#[async_trait]
88pub trait StateWriteExt: StateWrite {
89 fn put_auction_params(&mut self, params: AuctionParameters) {
91 self.object_put(state_key::parameters::updated_flag(), ());
92 self.put(state_key::parameters::key().into(), params)
93 }
94}
95
96impl<T: StateWrite + ?Sized> StateWriteExt for T {}
97
98pub(crate) trait AuctionCircuitBreaker: StateWrite {
135 #[instrument(skip(self))]
137 async fn auction_vcb_credit(&mut self, value: Value) -> Result<()> {
138 if value.amount == Amount::zero() {
139 tracing::trace!("short-circuit crediting zero-value");
140 return Ok(());
141 }
142
143 let prev_balance = self.get_auction_value_balance_for(&value.asset_id).await;
144 let new_balance = prev_balance.checked_add(&value.amount).ok_or_else(|| {
145 anyhow::anyhow!("overflowed balance while crediting auction circuit breaker (prev balance: {prev_balance:?}, credit: {value:?}")
146 })?;
147
148 tracing::trace!(
149 ?prev_balance,
150 ?new_balance,
151 "crediting the auction component VCB"
152 );
153
154 self.put(
156 state_key::value_balance::for_asset(&value.asset_id),
157 new_balance,
158 );
159 self.record_proto(event::auction_vcb_credit(
161 value.asset_id,
162 prev_balance,
163 new_balance,
164 ));
165 Ok(())
166 }
167
168 #[instrument(skip(self))]
170 async fn auction_vcb_debit(&mut self, value: Value) -> Result<()> {
171 if value.amount == Amount::zero() {
172 tracing::trace!("short-circuit debiting zero-value");
173 return Ok(());
174 }
175
176 let prev_balance = self.get_auction_value_balance_for(&value.asset_id).await;
177 let new_balance = prev_balance.checked_sub(&value.amount).ok_or_else(|| {
178 anyhow::anyhow!("underflowed balance while debiting auction circuit breaker (prev balance: {prev_balance:?}, debit={value:?}")
179 })?;
180
181 tracing::trace!(
182 ?prev_balance,
183 ?new_balance,
184 "debiting the auction component VCB"
185 );
186
187 self.put(
188 state_key::value_balance::for_asset(&value.asset_id),
189 new_balance,
190 );
191 self.record_proto(event::auction_vcb_debit(
193 value.asset_id,
194 prev_balance,
195 new_balance,
196 ));
197 Ok(())
198 }
199}
200
201impl<T: StateWrite + ?Sized> AuctionCircuitBreaker for T {}
202
203#[cfg(test)]
204mod tests {}