From 6f8f2cb8adc5a6f84df62def6690c84ad6e89ad6 Mon Sep 17 00:00:00 2001 From: Gabe Rodriguez Date: Fri, 1 Mar 2024 19:14:02 +0100 Subject: [PATCH] Add unfilled field to SimulateTradeResponse (#3917) See: https://github.com/penumbra-zone/web/issues/566 Would be nice if clients did not have to calculate this on their own by doing the math manually. --- .../core/component/dex/src/component/rpc.rs | 32 +++++++++++++----- .../src/gen/penumbra.core.component.dex.v1.rs | 3 ++ .../penumbra.core.component.dex.v1.serde.rs | 17 ++++++++++ .../proto/src/gen/proto_descriptor.bin.no_lfs | Bin 347616 -> 347803 bytes .../penumbra/core/component/dex/v1/dex.proto | 2 ++ 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/crates/core/component/dex/src/component/rpc.rs b/crates/core/component/dex/src/component/rpc.rs index 699cb097e5..6f218e0f6f 100644 --- a/crates/core/component/dex/src/component/rpc.rs +++ b/crates/core/component/dex/src/component/rpc.rs @@ -1,9 +1,11 @@ use std::{pin::Pin, sync::Arc}; -use crate::ExecutionCircuitBreaker; use async_stream::try_stream; -use cnidarium::{StateDelta, Storage}; use futures::{StreamExt, TryStreamExt}; +use tonic::Status; +use tracing::instrument; + +use cnidarium::{StateDelta, Storage}; use penumbra_asset::{asset, Value}; use penumbra_proto::{ core::component::dex::v1::{ @@ -20,18 +22,18 @@ use penumbra_proto::{ }, DomainType, StateReadProto, }; -use tonic::Status; -use tracing::instrument; -use super::{ - router::{RouteAndFill, RoutingParams}, - PositionRead, StateReadExt, -}; +use crate::ExecutionCircuitBreaker; use crate::{ lp::position::{self, Position}, state_key, DirectedTradingPair, SwapExecution, TradingPair, }; +use super::{ + router::{RouteAndFill, RoutingParams}, + PositionRead, StateReadExt, +}; + // TODO: Hide this and only expose a Router? pub struct Server { storage: Storage, @@ -541,7 +543,21 @@ impl SimulationService for Server { .await .map_err(|e| tonic::Status::internal(format!("error simulating trade: {:#}", e)))?; + let unfilled = Value { + amount: input + .amount + .checked_sub(&swap_execution.input.amount) + .ok_or_else(|| { + tonic::Status::failed_precondition( + "swap execution input amount is larger than request input amount" + .to_string(), + ) + })?, + asset_id: input.asset_id, + }; + Ok(tonic::Response::new(SimulateTradeResponse { + unfilled: Some(unfilled.into()), output: Some(swap_execution.into()), })) } diff --git a/crates/proto/src/gen/penumbra.core.component.dex.v1.rs b/crates/proto/src/gen/penumbra.core.component.dex.v1.rs index 67a87a958b..9ef3b955a2 100644 --- a/crates/proto/src/gen/penumbra.core.component.dex.v1.rs +++ b/crates/proto/src/gen/penumbra.core.component.dex.v1.rs @@ -1254,6 +1254,9 @@ impl ::prost::Name for SimulateTradeRequest { pub struct SimulateTradeResponse { #[prost(message, optional, tag = "1")] pub output: ::core::option::Option, + /// Estimated input amount that will not be swapped due to liquidity + #[prost(message, optional, tag = "2")] + pub unfilled: ::core::option::Option, } impl ::prost::Name for SimulateTradeResponse { const NAME: &'static str = "SimulateTradeResponse"; diff --git a/crates/proto/src/gen/penumbra.core.component.dex.v1.serde.rs b/crates/proto/src/gen/penumbra.core.component.dex.v1.serde.rs index 6fef673be7..ecd5756d47 100644 --- a/crates/proto/src/gen/penumbra.core.component.dex.v1.serde.rs +++ b/crates/proto/src/gen/penumbra.core.component.dex.v1.serde.rs @@ -4868,10 +4868,16 @@ impl serde::Serialize for SimulateTradeResponse { if self.output.is_some() { len += 1; } + if self.unfilled.is_some() { + len += 1; + } let mut struct_ser = serializer.serialize_struct("penumbra.core.component.dex.v1.SimulateTradeResponse", len)?; if let Some(v) = self.output.as_ref() { struct_ser.serialize_field("output", v)?; } + if let Some(v) = self.unfilled.as_ref() { + struct_ser.serialize_field("unfilled", v)?; + } struct_ser.end() } } @@ -4883,11 +4889,13 @@ impl<'de> serde::Deserialize<'de> for SimulateTradeResponse { { const FIELDS: &[&str] = &[ "output", + "unfilled", ]; #[allow(clippy::enum_variant_names)] enum GeneratedField { Output, + Unfilled, __SkipField__, } impl<'de> serde::Deserialize<'de> for GeneratedField { @@ -4911,6 +4919,7 @@ impl<'de> serde::Deserialize<'de> for SimulateTradeResponse { { match value { "output" => Ok(GeneratedField::Output), + "unfilled" => Ok(GeneratedField::Unfilled), _ => Ok(GeneratedField::__SkipField__), } } @@ -4931,6 +4940,7 @@ impl<'de> serde::Deserialize<'de> for SimulateTradeResponse { V: serde::de::MapAccess<'de>, { let mut output__ = None; + let mut unfilled__ = None; while let Some(k) = map_.next_key()? { match k { GeneratedField::Output => { @@ -4939,6 +4949,12 @@ impl<'de> serde::Deserialize<'de> for SimulateTradeResponse { } output__ = map_.next_value()?; } + GeneratedField::Unfilled => { + if unfilled__.is_some() { + return Err(serde::de::Error::duplicate_field("unfilled")); + } + unfilled__ = map_.next_value()?; + } GeneratedField::__SkipField__ => { let _ = map_.next_value::()?; } @@ -4946,6 +4962,7 @@ impl<'de> serde::Deserialize<'de> for SimulateTradeResponse { } Ok(SimulateTradeResponse { output: output__, + unfilled: unfilled__, }) } } diff --git a/crates/proto/src/gen/proto_descriptor.bin.no_lfs b/crates/proto/src/gen/proto_descriptor.bin.no_lfs index b2175122076e122b8a561ca2b0cfc9fc1bfad427..45487c1b7acbc76b2a89760d1bf267ef833f942e 100644 GIT binary patch delta 1996 zcmYL~UrZcT6vlV%pY1Yj5k}au%g*i?Ef(y8Wm$F?5^ee?qQ=CgYD{fvFknhTfu@U% zFDeyl(_knecxy{z;$t5)CPtz!V&a>ml7?U!B#3DUAw(>}Gz1BL=gv&$VIRJ8&+p7R z-<-Mo=grESw<)g-Uf8yC=^4!2UR8OsUoi{_=;f znnE-(!xFy=mW&^9j$bHtoog?4b~s%qJ{akN0G$j z-q(k$j{Z)k*z0t6o$v4J=qkQ%ntM%26~nq@nJ@Jd%45Fdbegcu{d-I%GFJGG2>mV$ zbGOMw2B~*Mq};4UYZ(@@#I$;%ipSNRNw!N(RdBO@TClyk5{{8QElT6QMB!;+C(Wld zjKjM^z4bGTt7IiFt-mYSV_v4A&&u46GRy#YmpXDmW{DXMCUZ1ThB3@D6VT+?jCe#d z&@)6or5_75tG=n^HEpwkrMw4nAhW`0@FgXQ#*9f0G+E($BJ`K%Koc_WiAdBuifT>d zz?`z9{CIlKpBq8s%=vReqS_qI-J}s5&a2@lZ%)tq4Ky*DcwRUe-2fzzdC{CRvl_v~ z3u-;ebLoWup)nT%gvMMTq*)_Si|UPiJm0n`Si&nyV=fB2#+Q^NYU}ivSu1>Bg#Piy z%p&u?h{VhUs%2@+B{jQ`C(}!U?a?@b$XOD5t9*%SOQJfVcOy&PEz@p1Q@89pgCIqH zXCQ$r`x}vEOifksByB?^1BJJ`;=ff$pev%xT_#AND*+0%s(!2Dwe_n3LgTIm2&W`M zh7CQGu*90GiSd!PHNhg@6avheD5>xzm^HDlN?X)yh1W&sU(cduWUh-y+)SfdGg-8u zc4EAtZNs++LF8=s7D1xg23bUVm9xSRMd*@rzpNZGABu==9zwMoRohhW#d)T2)31gg zayI>Hkf^pv)mk-z!!2w_JgrJ?9#gZ`yt22S|oHl;jcM%fMb^t-`sBf$JvBsSMA>(%fgpA)IgjO@h;QN4N(f9$C7&H>9 z_#Z1jg{ahF_dP(O#DFX**Y5!mB?e?grFJfFg$HHmGtap^G6!WOY35NqPtIMI%#PPN zubu65UOc8!Hm^Grf)NP2tBWKsm!-G5kf?rH!bvhBPn&i{mDv1P-4*G1hag3zcbP!~ zxgs%Bn?{)W+jhMxQD548fBLHQZnL~L@T&AS7ZT*vfP@-S4{ZKY+faazfkOd81`ZKI zdsDE&!!q=_H+=z_!xHz=3G`8*=||MZNq(|!M6yb49)idjksKj02Lb#E})F&za`YU4rLdJ~+2n{+$2zA|Jg|AEe z*SXu?g3Rj@*UR&0p@q7>Az4NIkUIT>jH$y8e=?1Ly9+o*_6=F${yRXT?hR=@Za$}B m9FD7N4nLe4_giVv-H*$%q#hX($oQvg4#!VStU2<*!T$jampHQk delta 1800 zcmYL~&r4ia6vt=oJ@<8diAG->oza849*cLiw!X$xJIkct?B$Y2u0ieNEQ5)mu*d+xl;Te$e1dp>9G_shA@ z#qZ+J7vp!PqCbi6@6yvp!qtNdGP(TgT`_+?dP{%2ByKG?m&BJHryl+lb>H$-B(kXV z zqepvWhrb#^0?=v*2|%k1oio1!wWdGokwYJ?kr)UDOCW2M9kr61=u*yQ&w0;zMv>=% z=R7)}QLM-P5YzJPc^zZ=3i?Gtp4Iz3()UqlS;jHC*R5qp%w1>8c?08cLtpEaMSsIy zB`?_W24!>RDv&@ns916f1`!@L^pCx=Ng45HMVW@!I3!&Rss^>wq1#WfY}|m)W64Nb_^2h1l`&VNU%H9*;*$^ zusb0u^?0(i8?k&rNT9nR%I)v=%iU$)i2}A}Q``yR zBv$mCeTw`WBvwS{KE?W6AJdAQSX2MfCr7JIn;44dY1+gfF|Emo@xqln=QTy{sMaGZ zq4PDx((X8>m3Z2L9`Bbq|G-W|5j_WX8YHG2@U(Xfg2O{y@0aiThc@|=N&e7oCM1wU zj^G1>Ao(Nxc}k{qZ%Ur`k8E@$llqa34iel^$Uz-5cXi}AL^$o^5aG0s8R7LT350z@ z`kVj6xIUAXPW8k_jw664)YfGphXi;+-Ek9A*>mox$k#zgWpv(Cv5Z^B%rb{Gr9Vx} zia(`-B}EZEQz}?eNKBhj2#Fn+d6}m5A8C2@(zFVmFN$1M!FoahnO3-Y)gTCIM&a#O z^zD@F)sq=H;-k>o&$3BsMg{u`33w)ip=R|D8F{Tb8zP+4Y>04DvyAXoOnA