Skip to content

Commit

Permalink
pcli: improve auction rendering (#4297)
Browse files Browse the repository at this point in the history
## Describe your changes

This improve rendering of query/view subcommands for DAs. Helpful for
testing and integration work.

Closes #4242, #4243, #4244

## Checklist before requesting a review

- [x] If this code contains consensus-breaking changes, I have added the
"consensus-breaking" label. Otherwise, I declare my belief that there
are not consensus-breaking changes, for the following reason:

  > Client changes
  • Loading branch information
erwanor authored May 1, 2024
1 parent 73c214d commit 982f089
Show file tree
Hide file tree
Showing 3 changed files with 223 additions and 30 deletions.
2 changes: 1 addition & 1 deletion crates/bin/pcli/src/command/query.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::{anyhow, Context, Result};

mod auction;
pub(crate) mod auction;
mod chain;
mod community_pool;
mod dex;
Expand Down
184 changes: 160 additions & 24 deletions crates/bin/pcli/src/command/query/auction.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
use crate::App;
use clap::Subcommand;
use comfy_table::{presets, Table};
use comfy_table::{Cell, ContentArrangement};
use penumbra_asset::asset::Cache;
use penumbra_asset::{asset, Value};
use penumbra_auction::auction::dutch::DutchAuction;
use penumbra_auction::auction::AuctionId;
use penumbra_dex::lp::position::{self, Position};
use penumbra_num::fixpoint::U128x128;
use penumbra_num::Amount;
use penumbra_proto::core::component::auction::v1alpha1 as pb_auction;
use penumbra_proto::core::component::auction::v1alpha1::query_service_client::QueryServiceClient;
use penumbra_proto::core::component::auction::v1alpha1::query_service_client::QueryServiceClient as AuctionQueryServiceClient;
use penumbra_proto::core::component::auction::v1alpha1::AuctionStateByIdRequest;
use penumbra_proto::core::component::dex::v1::query_service_client::QueryServiceClient as DexQueryServiceClient;
use penumbra_proto::core::component::dex::v1::LiquidityPositionByIdRequest;
use penumbra_proto::DomainType;
use penumbra_proto::Name;

Expand All @@ -23,8 +31,8 @@ impl AuctionCmd {
match self {
AuctionCmd::Dutch { auction_id } => {
let auction_id = auction_id.clone();
let mut client = QueryServiceClient::new(app.pd_channel().await?);
let rsp = client
let mut auction_client = AuctionQueryServiceClient::new(app.pd_channel().await?);
let rsp = auction_client
.auction_state_by_id(AuctionStateByIdRequest {
id: Some(auction_id.into()),
})
Expand All @@ -37,26 +45,24 @@ impl AuctionCmd {

if pb_auction_state.type_url == pb_auction::DutchAuction::type_url() {
let dutch_auction = DutchAuction::decode(pb_auction_state.value)?;
println!("dutch auction with id {auction_id:?}");

let mut table = Table::new();
table.load_preset(presets::NOTHING);
table
.set_header(vec![
"Auction Id",
"State",
"Start height",
"End height",
"Step count",
]) // TODO: make this more useful
.add_row(vec![
&auction_id.to_string(),
&render_state(dutch_auction.state.sequence),
&dutch_auction.description.start_height.to_string(),
&dutch_auction.description.end_height.to_string(),
&dutch_auction.description.step_count.to_string(),
]);
println!("{table}");
let position = if let Some(position_id) = dutch_auction.state.current_position {
let mut dex_client = DexQueryServiceClient::new(app.pd_channel().await?);
let position: Position = dex_client
.liquidity_position_by_id(LiquidityPositionByIdRequest {
position_id: Some(position_id.into()),
})
.await?
.into_inner()
.data
.expect("a position should exist")
.try_into()
.expect("no decoding error");
Some(position)
} else {
None
};

render_dutch_auction(&dutch_auction, position).await?;
} else {
unimplemented!("only supporting dutch auctions at the moment, come back later");
}
Expand All @@ -66,7 +72,107 @@ impl AuctionCmd {
}
}

fn render_state(state: u64) -> String {
pub async fn render_dutch_auction(
dutch_auction: &DutchAuction,
position: Option<Position>,
) -> anyhow::Result<()> {
let auction_id = dutch_auction.description.id();

println!("dutch auction with id {auction_id:?}");
if let Some(id) = position.as_ref() {
let position_id = id.id();
println!("auction has a deployed liquidity position with id: {position_id:?}");
}
let initial_input = dutch_auction.description.input;
let input_id = initial_input.asset_id;
let output_id = dutch_auction.description.output_id;

let _input_id_str = truncate_asset_id(&input_id);
let _output_id_str = truncate_asset_id(&output_id);

let initial_input_amount = U128x128::from(initial_input.amount);
let min_output = U128x128::from(dutch_auction.description.min_output);
let max_output = U128x128::from(dutch_auction.description.max_output);
let start_price = (max_output / initial_input_amount).expect("the input is always nonzero");
let end_price = (min_output / initial_input_amount).expect("the input is always nonzero");

let maybe_id = dutch_auction.state.current_position;

let (position_input_reserve, position_output_reserve) = position.as_ref().map_or_else(
|| (Amount::zero(), Amount::zero()),
|lp| {
(
lp.reserves_for(input_id).unwrap(),
lp.reserves_for(output_id).unwrap(),
)
},
);

let auction_input_reserves = Value {
amount: position_input_reserve + dutch_auction.state.input_reserves,
asset_id: input_id,
};
let auction_output_reserves = Value {
amount: position_output_reserve + dutch_auction.state.output_reserves,
asset_id: output_id,
};

let start_height = dutch_auction.description.start_height;
let end_height = dutch_auction.description.end_height;

let basic_cache = Cache::with_known_assets();

let mut auction_table = Table::new();
auction_table.load_preset(presets::ASCII_FULL);
auction_table
.set_header(vec![
"Auction id",
"State",
"Height range",
"# steps",
"Start price",
"End price",
"Input",
"Balance",
"Has lp?",
])
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
.add_row(vec![
Cell::new(&truncate_auction_id(&auction_id)).set_delimiter('.'),
Cell::new(&render_sequence(dutch_auction.state.sequence)),
Cell::new(&format!("{start_height} -> {end_height}")),
Cell::new(&dutch_auction.description.step_count.to_string()),
Cell::new(&format!("{}", start_price)),
Cell::new(&format!("{}", end_price)),
Cell::new(&initial_input.format(&basic_cache)),
Cell::new(format!(
"({}, {})",
&auction_input_reserves.format(&basic_cache),
&auction_output_reserves.format(&basic_cache)
)),
Cell::new(format!("{}", render_position_id(&maybe_id)))
.set_alignment(comfy_table::CellAlignment::Center),
]);

let mut position_table = Table::new();
position_table
.load_preset(presets::NOTHING)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_table_width(80)
.set_header(vec![
"position id",
"state",
"input reserves",
"output reserves",
"quoting price",
])
.add_row(vec![Cell::new("nothing for now")]);

println!("{auction_table}");
Ok(())
}

fn render_sequence(state: u64) -> String {
if state == 0 {
format!("Opened")
} else if state == 1 {
Expand All @@ -75,3 +181,33 @@ fn render_state(state: u64) -> String {
format!("Withdrawn (seq={state})")
}
}

fn truncate_asset_id(asset_id: &asset::Id) -> String {
let input = format!("{asset_id:?}");
let prefix_len = 15;
if input.len() > prefix_len {
format!("{}...", &input[..prefix_len])
} else {
input
}
}

fn truncate_auction_id(asset_id: &AuctionId) -> String {
let input = format!("{asset_id:?}");
let prefix_len = 16;
if input.len() > prefix_len {
format!("{}...", &input[..prefix_len])
} else {
input
}
}

fn render_position_id(maybe_id: &Option<position::Id>) -> String {
let input = maybe_id.map_or_else(|| format!("x"), |_| format!("✓"));
let prefix_len = 6;
if input.len() > prefix_len {
format!("{}..", &input[..prefix_len])
} else {
input
}
}
67 changes: 62 additions & 5 deletions crates/bin/pcli/src/command/view/auction.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
use anyhow::Result;
use comfy_table::{presets, Cell, ContentArrangement, Table};
use penumbra_auction::auction::dutch::DutchAuction;
use penumbra_keys::FullViewingKey;
use penumbra_proto::{core::component::auction::v1alpha1 as pb_auction, DomainType, Name};
use penumbra_view::ViewClient;

use crate::command::query::auction::render_dutch_auction;

#[derive(Debug, clap::Args)]
pub struct AuctionCmd {
#[clap(long)]
/// If set, includes the inactive auctions as well.
pub include_inactive: bool,
/// If set, make the view server query an RPC and pcli render the full auction state
#[clap(long, default_value_t = true)]
pub query_latest_state: bool,
}

impl AuctionCmd {
Expand All @@ -19,13 +27,62 @@ impl AuctionCmd {
view_client: &mut impl ViewClient,
_fvk: &FullViewingKey,
) -> Result<()> {
let auctions = view_client
.auctions(None, self.include_inactive, false)
let auctions: Vec<(
penumbra_auction::auction::AuctionId,
penumbra_view::SpendableNoteRecord,
Option<pbjson_types::Any>,
Vec<penumbra_dex::lp::position::Position>,
)> = view_client
.auctions(None, self.include_inactive, self.query_latest_state)
.await?;

auctions.iter().for_each(|(id, snr, _, _)| {
println!("{id:?} {}", snr.note.amount());
});
for (auction_id, _, maybe_auction_state, positions) in auctions.into_iter() {
if let Some(pb_auction_state) = maybe_auction_state {
if pb_auction_state.type_url == pb_auction::DutchAuction::type_url() {
let dutch_auction = DutchAuction::decode(pb_auction_state.value)
.expect("no deserialization error");
let position = positions.get(0).cloned();
render_dutch_auction(&dutch_auction, position)
.await
.expect("no rendering errors");
} else {
unimplemented!("only supporting dutch auctions at the moment, come back later");
}
} else {
let position_ids: Vec<String> = positions
.into_iter()
.map(|lp| format!("{}", lp.id()))
.collect();

let mut auction_table = Table::new();
auction_table.load_preset(presets::ASCII_FULL);
auction_table
.set_header(vec!["Auction id", "LPs"])
.set_content_arrangement(ContentArrangement::DynamicFullWidth)
.add_row(vec![
Cell::new(&auction_id).set_delimiter('.'),
Cell::new(format!("{:?}", position_ids))
.set_alignment(comfy_table::CellAlignment::Center),
]);

let mut position_table = Table::new();
position_table
.load_preset(presets::NOTHING)
.set_content_arrangement(ContentArrangement::Dynamic)
.set_table_width(80)
.set_header(vec![
"position id",
"state",
"input reserves",
"output reserves",
"quoting price",
])
.add_row(vec![Cell::new("nothing for now")]);

println!("{auction_table}");
println!("detected auction with")
}
}
Ok(())
}
}

0 comments on commit 982f089

Please sign in to comment.