diff --git a/crates/core/component/dex/src/component/action_handler/position/close.rs b/crates/core/component/dex/src/component/action_handler/position/close.rs index 9ba244126f..a13d8d8764 100644 --- a/crates/core/component/dex/src/component/action_handler/position/close.rs +++ b/crates/core/component/dex/src/component/action_handler/position/close.rs @@ -2,7 +2,7 @@ use anyhow::Result; use async_trait::async_trait; use cnidarium::StateWrite; use cnidarium_component::ActionHandler; -use penumbra_proto::StateWriteProto as _; +use penumbra_proto::{DomainType as _, StateWriteProto as _}; use crate::{component::PositionManager, event, lp::action::PositionClose}; @@ -25,7 +25,12 @@ impl ActionHandler for PositionClose { state.queue_close_position(self.position_id); // queue position close you will... - state.record_proto(event::queue_position_close(self)); + state.record_proto( + event::EventQueuePositionClose { + position_id: self.position_id, + } + .to_proto(), + ); Ok(()) } diff --git a/crates/core/component/dex/src/component/action_handler/swap.rs b/crates/core/component/dex/src/component/action_handler/swap.rs index 145262252e..8b69289ba0 100644 --- a/crates/core/component/dex/src/component/action_handler/swap.rs +++ b/crates/core/component/dex/src/component/action_handler/swap.rs @@ -5,7 +5,7 @@ use async_trait::async_trait; use cnidarium::StateWrite; use cnidarium_component::ActionHandler; use penumbra_proof_params::SWAP_PROOF_VERIFICATION_KEY; -use penumbra_proto::StateWriteProto; +use penumbra_proto::{DomainType as _, StateWriteProto}; use penumbra_sct::component::source::SourceContext; use crate::{ @@ -66,7 +66,7 @@ impl ActionHandler for Swap { ); state.add_recently_accessed_asset(swap.body.trading_pair.asset_2(), fixed_candidates); - state.record_proto(event::swap(self)); + state.record_proto(event::EventSwap::from(self).to_proto()); Ok(()) } diff --git a/crates/core/component/dex/src/component/action_handler/swap_claim.rs b/crates/core/component/dex/src/component/action_handler/swap_claim.rs index 48b35d228c..cf5408f305 100644 --- a/crates/core/component/dex/src/component/action_handler/swap_claim.rs +++ b/crates/core/component/dex/src/component/action_handler/swap_claim.rs @@ -7,7 +7,7 @@ use penumbra_txhash::TransactionContext; use cnidarium::{StateRead, StateWrite}; use penumbra_proof_params::SWAPCLAIM_PROOF_VERIFICATION_KEY; -use penumbra_proto::StateWriteProto; +use penumbra_proto::{DomainType as _, StateWriteProto}; use penumbra_sct::component::{ source::SourceContext, tree::{SctManager, VerificationExt}, @@ -95,7 +95,7 @@ impl ActionHandler for SwapClaim { state.nullify(self.body.nullifier, source).await; - state.record_proto(event::swap_claim(self)); + state.record_proto(event::EventSwapClaim::from(self).to_proto()); Ok(()) } diff --git a/crates/core/component/dex/src/component/arb.rs b/crates/core/component/dex/src/component/arb.rs index 9bb4b160ca..c6618d5756 100644 --- a/crates/core/component/dex/src/component/arb.rs +++ b/crates/core/component/dex/src/component/arb.rs @@ -5,7 +5,7 @@ use anyhow::Result; use async_trait::async_trait; use cnidarium::{StateDelta, StateWrite}; use penumbra_asset::{asset, Value}; -use penumbra_proto::StateWriteProto as _; +use penumbra_proto::{DomainType as _, StateWriteProto as _}; use penumbra_sct::component::clock::EpochRead; use tracing::instrument; @@ -134,7 +134,13 @@ pub trait Arbitrage: StateWrite + Sized { .await?; // Emit an ABCI event detailing the arb execution. - self_mut.record_proto(event::arb_execution(height, se)); + self_mut.record_proto( + event::EventArbExecution { + height, + swap_execution: se, + } + .to_proto(), + ); return Ok(Some(Value { amount: arb_profit, asset_id: arb_token, diff --git a/crates/core/component/dex/src/component/circuit_breaker/value.rs b/crates/core/component/dex/src/component/circuit_breaker/value.rs index 4708da46b0..bbe9785bad 100644 --- a/crates/core/component/dex/src/component/circuit_breaker/value.rs +++ b/crates/core/component/dex/src/component/circuit_breaker/value.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, Result}; use cnidarium::{StateRead, StateWrite}; use penumbra_asset::{asset, Value}; use penumbra_num::Amount; -use penumbra_proto::{StateReadProto, StateWriteProto}; +use penumbra_proto::{DomainType, StateReadProto, StateWriteProto}; use tonic::async_trait; use tracing::instrument; @@ -39,7 +39,14 @@ pub(crate) trait ValueCircuitBreaker: StateWrite { tracing::debug!(?prev_balance, ?new_balance, "crediting the dex VCB"); self.put(state_key::value_balance(&value.asset_id), new_balance); - self.record_proto(event::vcb_credit(value.asset_id, prev_balance, new_balance)); + self.record_proto( + event::EventValueCircuitBreakerCredit { + asset_id: value.asset_id, + previous_balance: prev_balance, + new_balance, + } + .to_proto(), + ); Ok(()) } @@ -61,7 +68,14 @@ pub(crate) trait ValueCircuitBreaker: StateWrite { tracing::debug!(?prev_balance, ?new_balance, "crediting the dex VCB"); self.put(state_key::value_balance(&value.asset_id), new_balance); - self.record_proto(event::vcb_debit(value.asset_id, prev_balance, new_balance)); + self.record_proto( + event::EventValueCircuitBreakerDebit { + asset_id: value.asset_id, + previous_balance: prev_balance, + new_balance, + } + .to_proto(), + ); Ok(()) } } diff --git a/crates/core/component/dex/src/component/dex.rs b/crates/core/component/dex/src/component/dex.rs index dfe982efb3..816a79f965 100644 --- a/crates/core/component/dex/src/component/dex.rs +++ b/crates/core/component/dex/src/component/dex.rs @@ -10,7 +10,7 @@ use penumbra_asset::{Value, STAKING_TOKEN_ASSET_ID}; use penumbra_fee::component::StateWriteExt as _; use penumbra_fee::Fee; use penumbra_num::Amount; -use penumbra_proto::{StateReadProto, StateWriteProto}; +use penumbra_proto::{DomainType as _, StateReadProto, StateWriteProto}; use tendermint::v0_37::abci; use tracing::instrument; @@ -399,11 +399,14 @@ pub(crate) trait InternalDexWrite: StateWrite { self.object_put(state_key::pending_outputs(), outputs); // Also generate an ABCI event for indexing: - self.record_proto(event::batch_swap( - output_data, - swap_execution_1_for_2, - swap_execution_2_for_1, - )); + self.record_proto( + event::EventBatchSwap { + batch_swap_output_data: output_data, + swap_execution_1_for_2, + swap_execution_2_for_1, + } + .to_proto(), + ); Ok(()) } diff --git a/crates/core/component/dex/src/component/position_manager.rs b/crates/core/component/dex/src/component/position_manager.rs index 0a1c9bcf29..5858f185c8 100644 --- a/crates/core/component/dex/src/component/position_manager.rs +++ b/crates/core/component/dex/src/component/position_manager.rs @@ -205,7 +205,7 @@ pub trait PositionManager: StateWrite + PositionRead { self.update_position(id, Some(prev_state), new_state) .await?; - self.record_proto(event::position_close_by_id(*id)); + self.record_proto(event::EventPositionClose { position_id: *id }.to_proto()); Ok(()) } @@ -279,7 +279,7 @@ pub trait PositionManager: StateWrite + PositionRead { self.mark_trading_pair_as_active(position.phi.pair); // Finally, record the new position state. - self.record_proto(event::position_open(&position)); + self.record_proto(event::EventPositionOpen::from(position.clone()).to_proto()); self.update_position(&id, None, position).await?; Ok(()) @@ -349,7 +349,9 @@ pub trait PositionManager: StateWrite + PositionRead { // We have already short-circuited no-op execution updates, so we can emit an execution // event and not worry about duplicates. - self.record_proto(event::position_execution(&prev_state, &new_state, context)); + self.record_proto( + event::EventPositionExecution::in_context(&prev_state, &new_state, context).to_proto(), + ); // Handle "close-on-fill": automatically flip the position state to "closed" if // either of the reserves are zero. @@ -363,7 +365,7 @@ pub trait PositionManager: StateWrite + PositionRead { ); new_state.state = position::State::Closed; - self.record_proto(event::position_close_by_id(position_id)); + self.record_proto(event::EventPositionClose { position_id }.to_proto()); } } @@ -431,7 +433,9 @@ pub trait PositionManager: StateWrite + PositionRead { // Record an event prior to updating the position state, so we have access to // the current reserves. - self.record_proto(event::position_withdraw(position_id, &prev_state)); + self.record_proto( + event::EventPositionWithdraw::in_context(position_id, &prev_state).to_proto(), + ); // Grab a copy of the final reserves of the position to return to the caller. let reserves = prev_state.reserves.balance(&prev_state.phi.pair); diff --git a/crates/core/component/dex/src/event.rs b/crates/core/component/dex/src/event.rs index a595dac5b3..5e2f052ead 100644 --- a/crates/core/component/dex/src/event.rs +++ b/crates/core/component/dex/src/event.rs @@ -1,143 +1,634 @@ use crate::{ - lp::{ - action::PositionClose, - position::{self, Position}, - }, + lp::position::{self, Position}, swap::Swap, swap_claim::SwapClaim, - BatchSwapOutputData, CandlestickData, DirectedTradingPair, SwapExecution, + BatchSwapOutputData, CandlestickData, DirectedTradingPair, SwapExecution, TradingPair, }; use anyhow::{anyhow, Context}; -use prost::Name; - use penumbra_asset::asset; use penumbra_num::Amount; use penumbra_proto::{penumbra::core::component::dex::v1 as pb, DomainType}; +use penumbra_sct::Nullifier; +use penumbra_tct::StateCommitment; +use prost::Name as _; + +#[derive(Clone, Debug)] +pub struct EventSwap { + pub trading_pair: TradingPair, + pub delta_1_i: Amount, + pub delta_2_i: Amount, + pub swap_commitment: StateCommitment, +} + +impl From for EventSwap { + fn from(value: Swap) -> Self { + Self::from(&value) + } +} + +impl From<&Swap> for EventSwap { + fn from(value: &Swap) -> Self { + Self { + trading_pair: value.body.trading_pair, + delta_1_i: value.body.delta_1_i, + delta_2_i: value.body.delta_2_i, + swap_commitment: value.body.payload.commitment, + } + } +} + +impl TryFrom for EventSwap { + type Error = anyhow::Error; + + fn try_from(value: pb::EventSwap) -> Result { + fn inner(value: pb::EventSwap) -> anyhow::Result { + Ok(EventSwap { + trading_pair: value + .trading_pair + .ok_or(anyhow!("missing `trading_pair`"))? + .try_into()?, + delta_1_i: value + .delta_1_i + .ok_or(anyhow!("missing `delta_1_i`"))? + .try_into()?, + delta_2_i: value + .delta_2_i + .ok_or(anyhow!("missing `delta_2_i`"))? + .try_into()?, + swap_commitment: value + .swap_commitment + .ok_or(anyhow!("missing `swap_commitment`"))? + .try_into()?, + }) + } + inner(value).context(format!("parsing {}", pb::EventSwap::NAME)) + } +} + +impl From for pb::EventSwap { + fn from(value: EventSwap) -> Self { + Self { + trading_pair: Some(value.trading_pair.into()), + delta_1_i: Some(value.delta_1_i.into()), + delta_2_i: Some(value.delta_2_i.into()), + swap_commitment: Some(value.swap_commitment.into()), + } + } +} + +impl DomainType for EventSwap { + type Proto = pb::EventSwap; +} + +#[derive(Clone, Debug)] +pub struct EventSwapClaim { + pub trading_pair: TradingPair, + pub output_1_commitment: StateCommitment, + pub output_2_commitment: StateCommitment, + pub nullifier: Nullifier, +} -pub fn swap(swap: &Swap) -> pb::EventSwap { - pb::EventSwap { - trading_pair: Some(swap.body.trading_pair.into()), - delta_1_i: Some(swap.body.delta_1_i.into()), - delta_2_i: Some(swap.body.delta_2_i.into()), - swap_commitment: Some(swap.body.payload.commitment.into()), +impl From for EventSwapClaim { + fn from(value: SwapClaim) -> Self { + Self::from(&value) } } -pub fn swap_claim(swap_claim: &SwapClaim) -> pb::EventSwapClaim { - pb::EventSwapClaim { - trading_pair: Some(swap_claim.body.output_data.trading_pair.into()), - output_1_commitment: Some(swap_claim.body.output_1_commitment.into()), - output_2_commitment: Some(swap_claim.body.output_2_commitment.into()), - nullifier: Some(swap_claim.body.nullifier.into()), +impl From<&SwapClaim> for EventSwapClaim { + fn from(value: &SwapClaim) -> Self { + Self { + trading_pair: value.body.output_data.trading_pair, + output_1_commitment: value.body.output_1_commitment, + output_2_commitment: value.body.output_2_commitment, + nullifier: value.body.nullifier, + } } } -pub fn position_open(position: &Position) -> pb::EventPositionOpen { - pb::EventPositionOpen { - position_id: Some(position.id().into()), - trading_pair: Some(position.phi.pair.into()), - reserves_1: Some(position.reserves.r1.into()), - reserves_2: Some(position.reserves.r2.into()), - trading_fee: position.phi.component.fee, - position: Some(position.clone().into()), +impl TryFrom for EventSwapClaim { + type Error = anyhow::Error; + + fn try_from(value: pb::EventSwapClaim) -> Result { + fn inner(value: pb::EventSwapClaim) -> anyhow::Result { + Ok(EventSwapClaim { + trading_pair: value + .trading_pair + .ok_or(anyhow!("missing `trading_pair`"))? + .try_into()?, + output_1_commitment: value + .output_1_commitment + .ok_or(anyhow!("missing `output_1_commitment`"))? + .try_into()?, + output_2_commitment: value + .output_2_commitment + .ok_or(anyhow!("missing `output_2_commitment`"))? + .try_into()?, + nullifier: value + .nullifier + .ok_or(anyhow!("missing `nullifier`"))? + .try_into()?, + }) + } + inner(value).context(format!("parsing {}", pb::EventSwapClaim::NAME)) + } +} + +impl From for pb::EventSwapClaim { + fn from(value: EventSwapClaim) -> Self { + Self { + trading_pair: Some(value.trading_pair.into()), + output_1_commitment: Some(value.output_1_commitment.into()), + output_2_commitment: Some(value.output_2_commitment.into()), + nullifier: Some(value.nullifier.into()), + } + } +} + +impl DomainType for EventSwapClaim { + type Proto = pb::EventSwapClaim; +} + +#[derive(Clone, Debug)] +pub struct EventPositionOpen { + pub position_id: position::Id, + pub trading_pair: TradingPair, + pub reserves_1: Amount, + pub reserves_2: Amount, + pub trading_fee: u32, + pub position: Position, +} + +impl From for EventPositionOpen { + fn from(value: Position) -> Self { + Self { + position_id: value.id(), + trading_pair: value.phi.pair, + reserves_1: value.reserves_1().amount, + reserves_2: value.reserves_2().amount, + trading_fee: value.phi.component.fee, + position: value, + } } } -pub fn position_close_by_id(id: position::Id) -> pb::EventPositionClose { - pb::EventPositionClose { - position_id: Some(id.into()), +impl TryFrom for EventPositionOpen { + type Error = anyhow::Error; + + fn try_from(value: pb::EventPositionOpen) -> Result { + fn inner(value: pb::EventPositionOpen) -> anyhow::Result { + Ok(EventPositionOpen { + position_id: value + .position_id + .ok_or(anyhow!("missing `position_id`"))? + .try_into()?, + trading_pair: value + .trading_pair + .ok_or(anyhow!("missing `trading_pair`"))? + .try_into()?, + reserves_1: value + .reserves_1 + .ok_or(anyhow!("missing `reserves_1`"))? + .try_into()?, + reserves_2: value + .reserves_2 + .ok_or(anyhow!("missing `reserves_2`"))? + .try_into()?, + trading_fee: value.trading_fee, + position: value + .position + .ok_or(anyhow!("missing `position`"))? + .try_into()?, + }) + } + inner(value).context(format!("parsing {}", pb::EventPositionOpen::NAME)) } } -pub fn position_close(action: &PositionClose) -> pb::EventPositionClose { - pb::EventPositionClose { - position_id: Some(action.position_id.into()), +impl From for pb::EventPositionOpen { + fn from(value: EventPositionOpen) -> Self { + Self { + position_id: Some(value.position_id.into()), + trading_pair: Some(value.trading_pair.into()), + reserves_1: Some(value.reserves_1.into()), + reserves_2: Some(value.reserves_2.into()), + trading_fee: value.trading_fee, + position: Some(value.position.into()), + } } } -pub fn queue_position_close(action: &PositionClose) -> pb::EventQueuePositionClose { - pb::EventQueuePositionClose { - position_id: Some(action.position_id.into()), +impl DomainType for EventPositionOpen { + type Proto = pb::EventPositionOpen; +} + +#[derive(Clone, Debug)] +pub struct EventPositionClose { + pub position_id: position::Id, +} + +impl TryFrom for EventPositionClose { + type Error = anyhow::Error; + + fn try_from(value: pb::EventPositionClose) -> Result { + fn inner(value: pb::EventPositionClose) -> anyhow::Result { + Ok(EventPositionClose { + position_id: value + .position_id + .ok_or(anyhow!("missing `position_id`"))? + .try_into()?, + }) + } + inner(value).context(format!("parsing {}", pb::EventPositionClose::NAME)) } } -pub fn position_withdraw( - position_id: position::Id, - final_position_state: &Position, -) -> pb::EventPositionWithdraw { - let sequence = if let position::State::Withdrawn { sequence, .. } = final_position_state.state { - sequence + 1 - } else { - 0 - }; - pb::EventPositionWithdraw { - position_id: Some(position_id.into()), - trading_pair: Some(final_position_state.phi.pair.into()), - reserves_1: Some(final_position_state.reserves.r1.into()), - reserves_2: Some(final_position_state.reserves.r2.into()), - sequence, +impl From for pb::EventPositionClose { + fn from(value: EventPositionClose) -> Self { + Self { + position_id: Some(value.position_id.into()), + } } } -pub fn position_execution( - prev_state: &Position, - new_state: &Position, - context: DirectedTradingPair, -) -> pb::EventPositionExecution { - pb::EventPositionExecution { - position_id: Some(new_state.id().into()), - trading_pair: Some(new_state.phi.pair.into()), - reserves_1: Some(new_state.reserves.r1.into()), - reserves_2: Some(new_state.reserves.r2.into()), - prev_reserves_1: Some(prev_state.reserves.r1.into()), - prev_reserves_2: Some(prev_state.reserves.r2.into()), - context: Some(context.into()), +impl DomainType for EventPositionClose { + type Proto = pb::EventPositionClose; +} + +#[derive(Clone, Debug)] +pub struct EventQueuePositionClose { + pub position_id: position::Id, +} + +impl TryFrom for EventQueuePositionClose { + type Error = anyhow::Error; + + fn try_from(value: pb::EventQueuePositionClose) -> Result { + fn inner(value: pb::EventQueuePositionClose) -> anyhow::Result { + Ok(EventQueuePositionClose { + position_id: value + .position_id + .ok_or(anyhow!("missing `position_id`"))? + .try_into()?, + }) + } + inner(value).context(format!("parsing {}", pb::EventQueuePositionClose::NAME)) } } -pub fn batch_swap( - bsod: BatchSwapOutputData, - swap_execution_1_for_2: Option, - swap_execution_2_for_1: Option, -) -> pb::EventBatchSwap { - pb::EventBatchSwap { - batch_swap_output_data: Some(bsod.into()), - swap_execution_1_for_2: swap_execution_1_for_2.map(Into::into), - swap_execution_2_for_1: swap_execution_2_for_1.map(Into::into), +impl From for pb::EventQueuePositionClose { + fn from(value: EventQueuePositionClose) -> Self { + Self { + position_id: Some(value.position_id.into()), + } } } -pub fn arb_execution(height: u64, swap_execution: SwapExecution) -> pb::EventArbExecution { - pb::EventArbExecution { - height, - swap_execution: Some(swap_execution.into()), +impl DomainType for EventQueuePositionClose { + type Proto = pb::EventQueuePositionClose; +} + +#[derive(Clone, Debug)] +pub struct EventPositionWithdraw { + pub position_id: position::Id, + pub trading_pair: TradingPair, + pub reserves_1: Amount, + pub reserves_2: Amount, + pub sequence: u64, +} + +impl EventPositionWithdraw { + /// Create this event using the usual context available to us. + pub fn in_context(position_id: position::Id, final_position_state: &Position) -> Self { + let sequence = + if let position::State::Withdrawn { sequence, .. } = final_position_state.state { + sequence + 1 + } else { + 0 + }; + Self { + position_id, + trading_pair: final_position_state.phi.pair, + reserves_1: final_position_state.reserves.r1, + reserves_2: final_position_state.reserves.r2, + sequence, + } } } -pub fn vcb_credit( - asset_id: asset::Id, - previous_balance: Amount, - new_balance: Amount, -) -> pb::EventValueCircuitBreakerCredit { - pb::EventValueCircuitBreakerCredit { - asset_id: Some(asset_id.into()), - previous_balance: Some(previous_balance.into()), - new_balance: Some(new_balance.into()), +impl TryFrom for EventPositionWithdraw { + type Error = anyhow::Error; + + fn try_from(value: pb::EventPositionWithdraw) -> Result { + fn inner(value: pb::EventPositionWithdraw) -> anyhow::Result { + Ok(EventPositionWithdraw { + position_id: value + .position_id + .ok_or(anyhow!("missing `position_id`"))? + .try_into()?, + trading_pair: value + .trading_pair + .ok_or(anyhow!("missing `trading_pair`"))? + .try_into()?, + reserves_1: value + .reserves_1 + .ok_or(anyhow!("missing `reserves_1`"))? + .try_into()?, + reserves_2: value + .reserves_2 + .ok_or(anyhow!("missing `reserves_2`"))? + .try_into()?, + sequence: value.sequence, + }) + } + inner(value).context(format!("parsing {}", pb::EventPositionWithdraw::NAME)) } } -pub fn vcb_debit( - asset_id: asset::Id, - previous_balance: Amount, - new_balance: Amount, -) -> pb::EventValueCircuitBreakerDebit { - pb::EventValueCircuitBreakerDebit { - asset_id: Some(asset_id.into()), - previous_balance: Some(previous_balance.into()), - new_balance: Some(new_balance.into()), +impl From for pb::EventPositionWithdraw { + fn from(value: EventPositionWithdraw) -> Self { + Self { + position_id: Some(value.position_id.into()), + trading_pair: Some(value.trading_pair.into()), + reserves_1: Some(value.reserves_1.into()), + reserves_2: Some(value.reserves_2.into()), + sequence: value.sequence, + } } } +impl DomainType for EventPositionWithdraw { + type Proto = pb::EventPositionWithdraw; +} + +#[derive(Clone, Debug)] +pub struct EventPositionExecution { + pub position_id: position::Id, + pub trading_pair: TradingPair, + pub reserves_1: Amount, + pub reserves_2: Amount, + pub prev_reserves_1: Amount, + pub prev_reserves_2: Amount, + pub context: DirectedTradingPair, +} + +impl EventPositionExecution { + /// Create this event using the usual context available to us. + pub fn in_context( + prev_state: &Position, + new_state: &Position, + context: DirectedTradingPair, + ) -> Self { + Self { + position_id: new_state.id(), + trading_pair: new_state.phi.pair, + reserves_1: new_state.reserves_1().amount, + reserves_2: new_state.reserves_2().amount, + prev_reserves_1: prev_state.reserves_1().amount, + prev_reserves_2: prev_state.reserves_2().amount, + context, + } + } +} + +impl TryFrom for EventPositionExecution { + type Error = anyhow::Error; + + fn try_from(value: pb::EventPositionExecution) -> Result { + fn inner(value: pb::EventPositionExecution) -> anyhow::Result { + Ok(EventPositionExecution { + position_id: value + .position_id + .ok_or(anyhow!("missing `position_id`"))? + .try_into()?, + trading_pair: value + .trading_pair + .ok_or(anyhow!("missing `trading_pair`"))? + .try_into()?, + reserves_1: value + .reserves_1 + .ok_or(anyhow!("missing `reserves_1`"))? + .try_into()?, + reserves_2: value + .reserves_2 + .ok_or(anyhow!("missing `reserves_2`"))? + .try_into()?, + prev_reserves_1: value + .prev_reserves_1 + .ok_or(anyhow!("missing `prev_reserves_1`"))? + .try_into()?, + prev_reserves_2: value + .prev_reserves_2 + .ok_or(anyhow!("missing `prev_reserves_2`"))? + .try_into()?, + context: value + .context + .ok_or(anyhow!("missing `context`"))? + .try_into()?, + }) + } + inner(value).context(format!("parsing {}", pb::EventPositionExecution::NAME)) + } +} + +impl From for pb::EventPositionExecution { + fn from(value: EventPositionExecution) -> Self { + Self { + position_id: Some(value.position_id.into()), + trading_pair: Some(value.trading_pair.into()), + reserves_1: Some(value.reserves_1.into()), + reserves_2: Some(value.reserves_2.into()), + prev_reserves_1: Some(value.prev_reserves_1.into()), + prev_reserves_2: Some(value.prev_reserves_2.into()), + context: Some(value.context.into()), + } + } +} + +impl DomainType for EventPositionExecution { + type Proto = pb::EventPositionExecution; +} + +#[derive(Clone, Debug)] +pub struct EventBatchSwap { + pub batch_swap_output_data: BatchSwapOutputData, + pub swap_execution_1_for_2: Option, + pub swap_execution_2_for_1: Option, +} + +impl TryFrom for EventBatchSwap { + type Error = anyhow::Error; + + fn try_from(value: pb::EventBatchSwap) -> Result { + fn inner(value: pb::EventBatchSwap) -> anyhow::Result { + Ok(EventBatchSwap { + batch_swap_output_data: value + .batch_swap_output_data + .ok_or(anyhow!("missing `batch_swap_output_data`"))? + .try_into()?, + swap_execution_1_for_2: value + .swap_execution_1_for_2 + .map(|x| x.try_into()) + .transpose()?, + swap_execution_2_for_1: value + .swap_execution_2_for_1 + .map(|x| x.try_into()) + .transpose()?, + }) + } + inner(value).context(format!("parsing {}", pb::EventBatchSwap::NAME)) + } +} + +impl From for pb::EventBatchSwap { + fn from(value: EventBatchSwap) -> Self { + Self { + batch_swap_output_data: Some(value.batch_swap_output_data.into()), + swap_execution_1_for_2: value.swap_execution_1_for_2.map(|x| x.into()), + swap_execution_2_for_1: value.swap_execution_2_for_1.map(|x| x.into()), + } + } +} + +impl DomainType for EventBatchSwap { + type Proto = pb::EventBatchSwap; +} + +#[derive(Clone, Debug)] +pub struct EventArbExecution { + pub height: u64, + pub swap_execution: SwapExecution, +} + +impl TryFrom for EventArbExecution { + type Error = anyhow::Error; + + fn try_from(value: pb::EventArbExecution) -> Result { + fn inner(value: pb::EventArbExecution) -> anyhow::Result { + Ok(EventArbExecution { + height: value.height, + swap_execution: value + .swap_execution + .ok_or(anyhow!("missing `swap_execution`"))? + .try_into()?, + }) + } + inner(value).context(format!("parsing {}", pb::EventArbExecution::NAME)) + } +} + +impl From for pb::EventArbExecution { + fn from(value: EventArbExecution) -> Self { + Self { + height: value.height, + swap_execution: Some(value.swap_execution.into()), + } + } +} + +impl DomainType for EventArbExecution { + type Proto = pb::EventArbExecution; +} + +#[derive(Clone, Debug)] +pub struct EventValueCircuitBreakerCredit { + pub asset_id: asset::Id, + pub previous_balance: Amount, + pub new_balance: Amount, +} + +impl TryFrom for EventValueCircuitBreakerCredit { + type Error = anyhow::Error; + + fn try_from(value: pb::EventValueCircuitBreakerCredit) -> Result { + fn inner( + value: pb::EventValueCircuitBreakerCredit, + ) -> anyhow::Result { + Ok(EventValueCircuitBreakerCredit { + asset_id: value + .asset_id + .ok_or(anyhow!("missing `asset_id`"))? + .try_into()?, + previous_balance: value + .previous_balance + .ok_or(anyhow!("missing `previous_balance`"))? + .try_into()?, + new_balance: value + .new_balance + .ok_or(anyhow!("missing `new_balance`"))? + .try_into()?, + }) + } + inner(value).context(format!( + "parsing {}", + pb::EventValueCircuitBreakerCredit::NAME + )) + } +} + +impl From for pb::EventValueCircuitBreakerCredit { + fn from(value: EventValueCircuitBreakerCredit) -> Self { + Self { + asset_id: Some(value.asset_id.into()), + previous_balance: Some(value.previous_balance.into()), + new_balance: Some(value.new_balance.into()), + } + } +} + +impl DomainType for EventValueCircuitBreakerCredit { + type Proto = pb::EventValueCircuitBreakerCredit; +} + +#[derive(Clone, Debug)] +pub struct EventValueCircuitBreakerDebit { + pub asset_id: asset::Id, + pub previous_balance: Amount, + pub new_balance: Amount, +} + +impl TryFrom for EventValueCircuitBreakerDebit { + type Error = anyhow::Error; + + fn try_from(value: pb::EventValueCircuitBreakerDebit) -> Result { + fn inner( + value: pb::EventValueCircuitBreakerDebit, + ) -> anyhow::Result { + Ok(EventValueCircuitBreakerDebit { + asset_id: value + .asset_id + .ok_or(anyhow!("missing `asset_id`"))? + .try_into()?, + previous_balance: value + .previous_balance + .ok_or(anyhow!("missing `previous_balance`"))? + .try_into()?, + new_balance: value + .new_balance + .ok_or(anyhow!("missing `new_balance`"))? + .try_into()?, + }) + } + inner(value).context(format!( + "parsing {}", + pb::EventValueCircuitBreakerDebit::NAME + )) + } +} + +impl From for pb::EventValueCircuitBreakerDebit { + fn from(value: EventValueCircuitBreakerDebit) -> Self { + Self { + asset_id: Some(value.asset_id.into()), + previous_balance: Some(value.previous_balance.into()), + new_balance: Some(value.new_balance.into()), + } + } +} + +impl DomainType for EventValueCircuitBreakerDebit { + type Proto = pb::EventValueCircuitBreakerDebit; +} + #[derive(Clone, Debug)] pub struct EventCandlestickData { pub pair: DirectedTradingPair,